Android Room中的实体列表关系

7

我想加载实体子列表,但是我希望避免进行2个查询。

我考虑在TypeConverter内部执行查询,但我真的不知道这是否是一个好主意。

我的实体:

@Entity
class Region(
        @PrimaryKey(autoGenerate = true)
        var id: Int = 0,
        var name: String = "",
        var locales: List<Locale> = listOf())

@Entity(foreignKeys = arrayOf(ForeignKey(
        entity = Region::class,
        parentColumns = arrayOf("id"),
        childColumns = arrayOf("regionId"),
        onDelete = CASCADE,
        onUpdate = CASCADE
)))
class Locale(
        @PrimaryKey(autoGenerate = true)
        var id: Int = 0,
        var regionId: Int = 0,
        var name: String = "")

DAOs :

@Dao
interface RoomRegionDao{
    @Insert
    fun insert(region: Region)

    @Delete
    fun delete(region: Region)

    @Query("select * from region")
    fun selectAll(): Flowable<List<Region>>
}

@Dao
interface RoomLocaleDao{
    @Insert
    fun insert(locale: Locale)

    @Query("select * from locale where regionId = :arg0")
    fun selectAll(regionId: Int): List<Locale>
}

数据库:

@Database(entities = arrayOf(Region::class, Locale::class), version = 1)
@TypeConverters(RoomAppDatabase.Converters::class)
abstract class RoomAppDatabase : RoomDatabase() {
    abstract fun regionDao(): RoomRegionDao
    abstract fun localeDao(): RoomLocaleDao

    inner class Converters {
        @TypeConverter
        fun toLocales(regionId: Int): List<Locale> {
            return localeDao().selectAll(regionId)
        }

        @TypeConverter
        fun fromLocales(locales: List<Locale>?): Int {
            locales ?: return 0
            if (locales.isEmpty()) return 0

            return locales.first().regionId
        }
    }
}

无法使用内部类作为转换器类,因此它无法工作。

  • 这是一个好方法吗?
  • 当我执行RoomRegionDao.selectAll时,如何在区域实体中自动加载“本地化列表”?

2
就在我快要放弃Kotlin和Room的时候,你的问题拯救了我。谢谢 :) - JustADev
3个回答

3
我认为TypeConverter只适用于静态方法。这是基于这里这里的示例。
根据这里的关系部分:
“因为SQLite是关系数据库,所以您可以指定对象之间的关系。即使大多数ORM库允许实体对象相互引用,Room也明确禁止这样做。”
因此,最好在locales属性上添加@Ignore,并在RoomLocaleDao上创建一个方法,该方法插入List<Locale>并在插入Region后调用它。
插入Region的方法可以返回插入的id
如果@Insert方法只接收1个参数,则可以返回long,该参数是插入项的新rowId。如果参数是数组或集合,则应返回long[]或List。 (https://developer.android.com/topic/libraries/architecture/room.html#daos-convenience)

谢谢您的回答。唯一的方法是制作高级接口,因此我制作了RegionDao,它使用RoomRegionDao和RoomLocaleDao来返回加载了本地语言的区域。 - Lyofen

1
你可以通过一个查询获取所有 Region,每个 Region 中包含一个 Locales 列表。
你只需要像这样创建一个新的 Relation
data class RegionWithLocale(
    @Embedded
    val region: Region,
    @Relation(
        parentColumn = "id",
        entity = Locale::class,
        entityColumn = "regionId"
    )
    val locales: List<Locale>
)

然后在你的 Dao 中获取这个关系,如下所示:

@Query("SELECT * FROM region WHERE id IS :regionId")
fun getRegions(regionId: Int): LiveData<RegionWithLocale>

@Query("SELECT * FROM region")
fun getAllRegions(): LiveData<List<RegionWithLocale>>

0

这个解决方案对我有用

ListConvert.kt

class ListConverter
{
    companion object
    {
        var gson = Gson()

        @TypeConverter
        @JvmStatic
        fun fromTimestamp(data: String?): List<Weather>?
        {
            if (data == null)
            {
                return Collections.emptyList()
            }

            val listType = object : TypeToken<List<String>>() {}.type
            return gson.fromJson(data, listType)
        }

        @TypeConverter
        @JvmStatic
        fun someObjectListToString(someObjects: List<Weather>?): String?
        {
            return gson.toJson(someObjects)
        }
    }
}

实体内的属性

@SerializedName("weather")
@TypeConverters(ListConverter::class)
val weather: List<Weather>,

不推荐这样做,因为如果需要,你无法直接从SQL查询Weather - Osvel Alvarez Jacomino

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接