【问题标题】:How to work this nested relationship in Room?如何在 Room 中处理这种嵌套关系?
【发布时间】:2021-10-13 12:21:57
【问题描述】:

我需要一点帮助。 我已经创建了所有的表,我可以创建一个关系来检索应用程序,但我不知道如何检索具有模型的车辆品牌列表。

@Entity(tableName = "application_table", indices = [Index(value = ["name"], unique = true)])
data class ApplicationItem(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id") val id: Int? = 0,
    @ColumnInfo(name = "name") val name: String
)
data class ApplicationWithBrandAndModel(
    @Embedded val application: ApplicationItem,
    @Relation(
        entity = BrandOfVehicleItem::class,
        parentColumn = "userId",
        entityColumn = "brandId"
    )
    val brands: List<BrandWithModel>
)
data class BrandWithModel(
    @Embedded val brand: BrandOfVehicleItem,
    @Relation(
        parentColumn = "path",
        entityColumn = "brandCreatorId"
    )
    val models: List<ModelOfVehicleItem>
)

【问题讨论】:

    标签: android sqlite kotlin android-room database-relations


    【解决方案1】:

    简而言之,您需要利用 AppBrandCrossRef 表来引用(associate)ApplicationItem 与 Brand 以获取 BrandWithModel 的列表。

    Room 使用的关键字是 associateBy,因此在 @Relation 中您需要使用 associateBy 参数指定关联。

    associateBy 参数本身采用 Junction 参数,您可以在其中定义交叉引用表和相应的列。

    所以我相信你想要:-

    data class ApplicationWithBrandAndModel(
        @Embedded val application: ApplicationItem,
    
        /*
        @Relation(
            entity = Brand::class,
            parentColumn = "userId", ??????? 
            entityColumn = "brandId"
        )
    
         */
        @Relation(
            entity = Brand::class,
            parentColumn = "id",
            entityColumn = "id",
            associateBy = Junction(
                value = ApplicationBrandCrossRef::class,
                parentColumn = "appId", // column in cross ref table that references the parent (application)
                entityColumn = "brandId" // column in cross ref table that references the child (brand)
            )
        )
        val brands: List<BrandWithModel>
    )
    

    以下是一个工作示例。

    • 请注意,已经进行了更改,因为您有很长的引用字符串,并且您还拥有(如上所述)一个 userId,它不在您的图表中。

    所以用来演示的实体是:-

    型号

    @Entity(
        indices = [Index("brandCreatorId", unique = false)],
        foreignKeys = [
            ForeignKey(
                entity = Brand::class,
                parentColumns = ["id"],
                childColumns = ["brandCreatorId"],
                onDelete = ForeignKey.CASCADE,
                onUpdate = ForeignKey.CASCADE)
        ]
    )
    data class Model(
        @PrimaryKey
        val id: Long? = null,
        val path: String,
        val code: String,
        val value: String,
        val brandCreatorId: Long
    )
    

    品牌

    @Entity(tableName = "brand_table")
    data class Brand(
        @PrimaryKey
        val id: Long? = null,
        val path: String,
        val code: String,
        val value: String
    )
    

    应用程序项

    @Entity(tableName = "application_table", indices = [Index(value = ["name"], unique = true)])
    data class ApplicationItem(
        @PrimaryKey
        @ColumnInfo(name = "id") val id: Long? = null,
        @ColumnInfo(name = "name") val name: String
    )
    

    ApplicationBrandCrossRef

    @Entity(
        primaryKeys = ["appId","brandId"],
        indices = [ Index(value = ["brandId"])],
        foreignKeys = [
            ForeignKey(
                entity = ApplicationItem::class,
                parentColumns = ["id"],
                childColumns = ["appId"],
                onDelete =  ForeignKey.CASCADE,
                onUpdate = ForeignKey.CASCADE),
            ForeignKey(
                entity = Brand::class,
                parentColumns = ["id"],
                childColumns = ["brandId"],
                onDelete = ForeignKey.CASCADE,
                onUpdate = ForeignKey.CASCADE
            )
        ]
    )
    data class ApplicationBrandCrossRef(
        val appId: Long,
        val brandId: Long
    )
    

    BrandWithModel

    data class BrandWithModel(
        @Embedded val brand: Brand,
        @Relation(
            parentColumn = "id",
            entityColumn = "brandCreatorId"
        )
        val models: List<Model>
    )
    

    ApplicationWithBrandAndModel

    data class ApplicationWithBrandAndModel(
        @Embedded val application: ApplicationItem,
    
        /*
        @Relation(
            entity = Brand::class,
            parentColumn = "userId", ???????
            entityColumn = "brandId"
        )
    
         */
        @Relation(
            entity = Brand::class,
            parentColumn = "id",
            entityColumn = "id",
            associateBy = Junction(
                value = ApplicationBrandCrossRef::class,
                parentColumn = "appId", // column in cross ref table that references the parent (application)
                entityColumn = "brandId" // column in cross ref table that references the child (brand)
            )
        )
        val brands: List<BrandWithModel>
    )
    

    AllDao 中的 Dao 是:-

    @Dao
    abstract class AllDao {
    
        @Insert
        abstract fun insert(brand: Brand): Long
        @Insert
        abstract fun insert(model: Model): Long
        @Insert
        abstract fun insert(applicationItem: ApplicationItem): Long
        @Insert
        abstract fun insert(applicationBrandCrossRef: ApplicationBrandCrossRef)
    
        @Query("SELECT * FROM application_table")
        @Transaction
        abstract fun getApplicationItemWithBrandAndTheModels(): List<ApplicationWithBrandAndModel>
    }
    

    以下是用来测试的:-

        db = TheDatabase.getInstance(this)
        dao = db.getHymnDao()
        allDao = db.getAllDao()
    
        var ai1 = allDao.insert(ApplicationItem(name = "A1"))
        var ai2 = allDao.insert(ApplicationItem(name = "A2"))
        var ai3 = allDao.insert(ApplicationItem(name = "A3"))
        var ai4 = allDao.insert(ApplicationItem(name = "A4"))
        var b1 = allDao.insert(Brand(path = "patha", code = "codea", value = "vala"))
        var b2 = allDao.insert(Brand(path = "pathb",code = "codeb",value = "valb"))
        var b3 = allDao.insert(Brand(path = "pathc",code = "codec",value = "valc"))
        allDao.insert(Model(path = "ma",code = "ma",value = "ma",brandCreatorId = b1))
        allDao.insert(Model(path = "mb", code = "mb", value = "mb", brandCreatorId = b1))
        allDao.insert(Model(path = "mc",code = "mc", value = "mc",brandCreatorId = b2))
        allDao.insert(Model(path = "md",code = "md", value = "md", brandCreatorId = b2))
        allDao.insert(Model(path = "me", code = "me", value = "me", brandCreatorId = b2))
        allDao.insert(ApplicationBrandCrossRef(ai1,b2))
        allDao.insert(ApplicationBrandCrossRef(ai1,b3))
        allDao.insert(ApplicationBrandCrossRef(ai2,b1))
        allDao.insert(ApplicationBrandCrossRef(ai3,b1))
        allDao.insert(ApplicationBrandCrossRef(ai3,b2))
        allDao.insert(ApplicationBrandCrossRef(ai3,b3))
        for(a: ApplicationWithBrandAndModel in allDao.getApplicationItemWithBrandAndTheModels()) {
            Log.d(TAG,"AppItem is ${a.application.name}")
            for(b: BrandWithModel in a.brands) {
                Log.d(TAG,"\tBrand is ${b.brand.code}")
                for(m: Model in b.models) {
                    Log.d(TAG,"\t\tModel is ${m.code}")
                }
            }
        }
    

    结果输出到日志:-

    D/APPINFO: AppItem is A1
    D/APPINFO:  Brand is codeb
    D/APPINFO:      Model is mc
    D/APPINFO:      Model is md
    D/APPINFO:      Model is me
    D/APPINFO:  Brand is codec
    D/APPINFO: AppItem is A2
    D/APPINFO:  Brand is codea
    D/APPINFO:      Model is ma
    D/APPINFO:      Model is mb
    D/APPINFO: AppItem is A3
    D/APPINFO:  Brand is codea
    D/APPINFO:      Model is ma
    D/APPINFO:      Model is mb
    D/APPINFO:  Brand is codeb
    D/APPINFO:      Model is mc
    D/APPINFO:      Model is md
    D/APPINFO:      Model is me
    D/APPINFO:  Brand is codec
    D/APPINFO: AppItem is A4
    
    • 即预期结果

    【讨论】:

    • 太好了,感谢您的帮助,我还有一个问题,是否必须放置级联删除?如果我在 ApplicationBrandCrossRef 类中理解正确,如果 ApplicationBrandCrossRef 中的项目并删除应用程序并删除?
    • onUpdate/onDelete 不是强制性的,但可能很有用 CASCADE 是最有可能有用的选项。见sqlite.org/foreignkeys.html#fk_actions。如果您觉得这个答案有用,请勾选它作为答案。
    猜你喜欢
    • 2018-03-05
    • 2018-02-18
    • 2019-01-16
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2017-03-05
    • 2020-02-12
    相关资源
    最近更新 更多