如何在 Room 中处理这种嵌套关系?

How to work this nested relationship in Room?

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

@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>
)

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

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

associateBy 参数本身带有一个 Junction 参数,您可以在其中定义交叉引用 table 和相应的列。

所以我相信你想要:-

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>
)

下面是一个工作示例。

  • 请注意,已进行更改,因为您有 long 的引用字符串,并且您还有(如上文注释)用户 ID,这不在您的图表中。

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

型号

@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
)

ApplicationItem

@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>
)

Dao 在 AllDao 中是:-

@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
  • 即预期结果