Room 数据库中的多对多关系。 Kotlin Junction 类被忽略

Posted

技术标签:

【中文标题】Room 数据库中的多对多关系。 Kotlin Junction 类被忽略【英文标题】:many-to-many relation in Room database. Kotlin Junction class ignored 【发布时间】:2020-05-11 14:27:07 【问题描述】:

我有以下用于 Room 版本的 Kotlin 实体。 2.2.5。

父实体

@Entity(tableName = "ITEM",
    indices = [Index(value = ["id"], unique = true), Index(value = ["code"], unique = true), Index(value = ["status"], unique = false)]
)
data class Item (
    @PrimaryKey @ColumnInfo(name = "id") override val id: UUID = UUID.randomUUID(),
    @ColumnInfo(name = "code") @NotNull val code: String,
    @ColumnInfo(name = "valid") val valid: Boolean = true,
    @ColumnInfo(name = "value") val value: Double?,
    @ColumnInfo(name = "price") val price: Double?,
    @ColumnInfo(name = "default_description") val defaultDescription: String?,
    @ColumnInfo(name = "description") val description: String?
)

子实体

@Entity(tableName = "LOCATION",
    indices = [Index(value = ["id"], unique = true), Index(value = ["code"], unique = true)]
)
data class Location (
    @ColumnInfo(name = "id") @PrimaryKey override val id: UUID = UUID.randomUUID(),
    @ColumnInfo(name = "code") val code: String,
    @ColumnInfo(name = "latitude") val latitude: Double?,
    @ColumnInfo(name = "longitude") val longitude: Double?,
    @ColumnInfo(name = "default_description") val defaultDescription: String?
)

连接实体

@Entity(
    tableName = "ITEM_LOCATION_L",
    primaryKeys = [
        "item_id", "location_id"
    ],
    foreignKeys = [
        ForeignKey(entity = Item::class, parentColumns = ["id"], childColumns = ["item_id"]),
        ForeignKey(entity = Location::class, parentColumns = ["id"], childColumns = ["location_id"])
    ],
    indices = [
        Index("id"),
        Index("item_id"),
        Index("location_id")])
data class ItemLocationLink (
    @ColumnInfo(name = "id") override val id: UUID = UUID.randomUUID(),
    @ColumnInfo(name = "item_id") val itemId: UUID, /** Item ID - parent entity */
    @ColumnInfo(name = "location_id") val locationId: UUID, /** Location ID - child entity */
    @ColumnInfo(name = "quantity") val quantity: Double /** Quantity of the item in the referenced location */
)

结果分类

class ItemLocationRelation 
    @Embedded
    lateinit var item: Item
    @Relation(
        parentColumn = "id",
        entityColumn = "id",
        associateBy = Junction(value = ItemLocationLink::class, parentColumn = "item_id", entityColumn = "location_id")
    ) lateinit var locations: List<Location>

DAO 接口

@Dao
interface ItemLocationLinkDao 

   @Transaction
    @Query("SELECT * FROM ITEM WHERE id = :itemId")
    fun getLocationsForItem(itemId: UUID): List<ItemLocationRelation>

数据库类型转换器

class DBTypesConverter 

    @TypeConverter
    fun fromUUID(uid: UUID?): String? 

        if (uid != null)
            return uid.toString()
        return null
    

    @TypeConverter
    fun toUUID(str: String?): UUID? 

        if (str != null) 
            return UUID.fromString(str)
        
        return null
    

    @TypeConverter
    fun fromDate(d: Date?): Long? 

        return d?.time
    

    @TypeConverter
    fun toDate(l: Long?): Date? 

        return if (l != null) Date(l) else null
    

当我调用 getLocationsForItem 时,我得到一个 ItemLocationRelation 实例,其中包含有效的 Item 但没有子对象。我检查了生成的代码,没有 Junction 类的迹象。生成的代码表现得好像它不是多对多关系,完全忽略了 Junction 类,我什至可以在关系的 @Junction 属性中指定一个假类,结果将完全相同,没有错误。

如果我向 DAO 类添加一个返回以下查询结果的函数:

select * from item 
inner join item_location_l as link on link.item_id = item.id
inner join LOCATION as l on link.location_id = l.id
where item.id = '99a3a64f-b0e6-e911-806a-68ecc5bcbe06'

我按预期得到 2 行。所以 SQLite 数据库没问题。请帮忙。

【问题讨论】:

我没有看到您的代码存在明显问题(除了:1. 我没有得到关于 2 的数据类的某些字段的覆盖内容。目前尚不清楚 UUID 的 typeConverter 是什么你用过吗)。恐怕如果没有完整的回购,有人帮助你将是一项复杂的任务 顺便问一下,您手动检查过您的查询结果吗?最简单的方法 - 是在您的 item_id 上使用内部连接对所有 3 个表构建查询 嗨,Sergiy,UUID 的类型转换器将 UUID 转换为字符串并返回。它工作正常。我也尝试用 long 替换 UUID 无济于事。我按照您的建议尝试了查询,它工作正常。问题是生成的代码完全缺少 Junction 表,因为它只是一个一对多的关系。我在问题代码中添加了类型转换器 所以你说在你生成的DAO实现类中没有提到表“ITEM_LOCATION_L”(用文本搜索)? 我调试了代码,没有任何地方使用 ITEM_LOCATION_L 的迹象。我调试了自动生成的代码,它的行为就像关系是一对一的。我也尝试以这种方式更改连接:Junction(value = String::class, parentColumn = "item_id", entityColumn = "location_id"),强制 String 作为连接类。生成的代码完全相同,我没有收到任何错误。结果与之前完全相同...该项目很好,但它没有子对象,因为生成的代码使用项目 ID 进行搜索,而不是在 ITEM_LOCATION_L 中查询正确的 ID 【参考方案1】:

所以,最后问题真的很微妙。由于某些原因,我无法解释我正在从事的项目有以下 gradle 设置(app gradle):

kapt "android.arch.persistence.room:compiler:2.2.5"

我把它换成了

kapt "androidx.room:room-compiler:2.2.5"

然后一切都很好。插件生成的硬编码查询现在包含 JOIN 并且可以正常工作....

【讨论】:

以上是关于Room 数据库中的多对多关系。 Kotlin Junction 类被忽略的主要内容,如果未能解决你的问题,请参考以下文章

Firebase 中的多对多关系

数据库模式中的多对多关系表是不是有正式名称?

GraphQL Apollo 中的多对多关系

SQL Server Analysis Services 中的多对多关系;第二个多对多关系不起作用

MongoDB中的多对多关系

建模数据仓库中的多对多关系