如何在 Room 中处理这种嵌套关系?
Posted
技术标签:
【中文标题】如何在 Room 中处理这种嵌套关系?【英文标题】:How to work this nested relationship in 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>
)
【问题讨论】:
【参考方案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。如果您觉得这个答案有用,请勾选它作为答案。以上是关于如何在 Room 中处理这种嵌套关系?的主要内容,如果未能解决你的问题,请参考以下文章