Android Room中如何插入Product与ProductList关系且不使用多对多?
结论先行
你无法在不修改Product实体的前提下用一对多实现这个关联需求,一对多的关系实现要求「多」侧实体必须持有「一」侧实体的外键,你不想在Product中加外键的话,最适合你的方案就是用中间表的多对多实现,完全符合你提出的「Product不需要感知所属列表」的要求。
你目前写的ProductListWithProducts的@Relation注解是无效的,parentColumn = "productListId"和entityColumn = "productId"没有任何关联逻辑,Room不知道怎么匹配两个表的数据,你看到的示例Dao插入方法也只是单纯往两个表插入数据,根本没有建立任何关联。
方案1:坚持用一对多(需要修改Product实体)
如果一定要用一对多,你需要给Product加一个可空的外键字段,用来存储所属的ProductList的id,可空的设计也支持Product不属于任何列表的场景:
@Entity(tableName = "products") data class Product( @PrimaryKey(autoGenerate = true) val productId: Long, val productName: String, // 新增可空外键,不需要感知的话可以不用对外暴露这个字段的访问方法 val belongToListId: Long? = null )
对应的关联类修改为:
data class ProductListWithProducts( @Embedded val productList: ProductList, @Relation( parentColumn = "productListId", entityColumn = "belongToListId" ) val products: List<Product> )
插入逻辑:先插入ProductList拿到生成的列表id,再给要关联的所有Product设置belongToListId后插入/更新即可。
方案2:用中间表的多对多(无需修改Product实体,更推荐)
这个方案完全不需要改动你现有的Product和ProductList实体,关联逻辑完全封装在中间表中,Product完全感知不到所属列表的存在,耦合度更低。
你已经定义好了中间表ProductListProducts,只需要修改关联类和Dao即可:
关联类修改:
data class ProductListWithProducts( @Embedded val productList: ProductList, @Relation( parentColumn = "productListId", entityColumn = "productId", associateBy = Junction(ProductListProducts::class) ) val products: List<Product> )
Dao插入实现:
@Dao abstract class ProductListDao { @Insert abstract fun insertProductList(productList: ProductList): Long @Insert abstract fun insertRelations(relations: List<ProductListProducts>) @Transaction fun insertListWithRelatedProducts(productList: ProductList, relatedProducts: List<Product>) { // 插入列表拿到生成的列表id val listId = insertProductList(productList) // 批量生成中间表关联数据插入即可 val relations = relatedProducts.map { ProductListProducts(productId = it.productId, productListId = listId) } insertRelations(relations) } }
如果你的Product已经提前插入到数据库了,直接传入对应的Product实例或者productId列表生成关联数据即可。
内容的提问来源于stack exchange,提问作者Ivan Sakharovskii

