【问题标题】:Android Room Embedded Field Not CompilingAndroid Room Embedded Field 未编译
【发布时间】:2021-12-06 15:27:48
【问题描述】:

我正在尝试创建一个嵌入式字段。这是一个简单的例子,但我无法让这个简单的例子工作。最终我需要有 3 个级别的嵌入式项目,但试图让这个测试用例工作。

@Entity(tableName = "userItemsEntity")
@Parcelize
data class Item(
    var objecttype: String?,
    @PrimaryKey(autoGenerate = false)
    var objectid: Int?,
    var subtype: String?,
    var collid: Int?,
    @Embedded
    var name: Name?
) : Parcelable

@Parcelize
data class Name(
    var primary: Boolean? = true,
    var sortindex: Int? = null,
    var content: String? = null) : Parcelable

当我尝试编译它时,它在 DAO 上抱怨 updateItem()

SQL error or missing database (no such column: name) 

DAO 函数

@Query("UPDATE userItemsEntity SET " +
    "objecttype=:objecttype, objectid=:objectid, subtype=:subtype, collid=:collid, name=:name " +
    "WHERE objectid=:objectid")
fun updateItem(
    objecttype: String?,
    objectid: Int,
    subtype: String?,
    collid: Int?,
    name: Name?)

【问题讨论】:

    标签: entity android-room android-database android-room-embedded


    【解决方案1】:

    原因是它说没有 name 列。而是根据 EMBEDDED 类的成员变量(即主要、排序索引和内容),该表由列组成。

    即表创建 SQL 是/将是:-

    CREATE TABLE IF NOT EXISTS `userItemsEntity` (`objecttype` TEXT, `objectid` INTEGER, `subtype` TEXT, `collid` INTEGER, `primary` INTEGER, `sortindex` INTEGER, `content` TEXT, PRIMARY KEY(`objectid`))
    

    Room 知道在提取行时从这些列构建相应的 Name 对象。

    所以你可以使用:-

    @Query("UPDATE userItemsEntity SET " +
            "objecttype=:objecttype, objectid=:objectid, subtype=:subtype, collid=:collid, `primary`=:primary, sortindex=:sortindex, content=:content " +
            "WHERE objectid=:objectid")
    fun updateItem(
        objecttype: String?,
        objectid: Int,
        subtype: String?,
        collid: Int?,
        primary: Boolean?,
        sortindex: Int?,
        content: String?
    )
    
    • 请注意,primary 是一个 SQLite 标记,因此用重音符号括起来以确保它不被视为标记。否则你会得到:-

      There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (near "primary": syntax error)
      

    但是,由于您使用的是基于主键 (objectid) 的 WHERE 子句,因此更新将仅适用于单行,因此您可以简单地使用:-

    @Update
    fun update(item: Item): Int
    
    • 显然函数的名称不必是update,它可以是任何合适的有效名称。
    • 这不仅具有更简单的优点,而且还可以返回更新的行数(如果行存在则为 1,否则为 0)

    实施名称列

    如果您想要一个 name 列并且该 name 列包含一个 Name 对象。然后,由于 SQLite 没有对象的存储/列类型,因此您不会嵌入 Name 类。

    您将拥有var name: Name? 带有适当的 TypeConverter,它将 Name 对象转换为 SQLite 支持的类型:-

    • 文本(字符串),

    • REAL(浮点数,双精度...),

    • INTEGER (Long, Int ...) 或

    • BLOB (ByteArray))。

    • 通常使用字符串,通常使用 GSON 将对象转换为 JOSN 字符串。

    • SQlite 确实有一个 NUMERIC 类型。但是,Room 不支持它的使用。我相信,因为其他类型涵盖了所有类型的数据,而 NUMERIC 是一个包罗万象/默认设置。

    但是,使用对象的 JSON 表示会引入膨胀并降低从 SQL 方面转换数据的有用性。

    例如说你有:-

    @Entity(tableName = "userOtherItemsEntity")
    @Parcelize
    data class OtherItem (
        var objecttype: String?,
        @PrimaryKey(autoGenerate = false)
        var objectid: Int?,
        var subtype: String?,
        var collid: Int?,
        var name: OtherName?) : Parcelable
    @Parcelize
    data class OtherName(
        var primary: Boolean? = true,
        var sortindex: Int? = null,
        var content: String? = null) : Parcelable
    

    那么基础表确实有 name 列。 Room 生成的 CREATE SQL 将是:-

    CREATE TABLE IF NOT EXISTS `userOtherItemsEntity` (`objecttype` TEXT, `objectid` INTEGER, `subtype` TEXT, `collid` INTEGER, `name` TEXT, PRIMARY KEY(`objectid`))
    

    但是,您需要 TypeConverters,它可以是:-

    @TypeConverter
    fun fromOtherName(othername: OtherName ): String {
        return Gson().toJson(othername)
    }
    @TypeConverter
    fun toOtherName(json: String): OtherName {
        return Gson().fromJson(json,OtherName::class.java)
    }
    
    • 第一个使用 Gson 将对象转换为 JSON 字符串,例如插入数据时
    • 第二个将 JSON 字符串转换为 OtherName 对象。

    使用嵌入名称的项目,然后数据将按照以下行存储:-

    在转换带有 OtherName 的 OtherItem 时,数据(类似数据)将沿着以下行:-

    • 在前者中,3 个名称列将占用大约 (1 + 1 + 12) = 16 个字节。

    • 在后者中,OtherName 列(在使用时不考虑“Other”一词)将占用大约 55 个字节。

    • 如果要将 OtherName 的组件包含在搜索中,后者可能需要更复杂且资源消耗更大的搜索。

      • 例如@Query("SELECT * FROM userItemsEntity WHERE primary") 而不是 @Query("SELECT * FROM userOtherItemsEntity WHERE instr(name,'primary\":true') > 0")

    【讨论】:

    • 哇,这是一个解释!谢谢你现在我明白了。我对此有点迷茫。我采用了 TypeConverters 的路线,但由于必须使用平均值对字段进行计算,因此希望它更多地基于 DB,因此使用 SQL 更容易做到这一点。谢谢和干杯
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-12
    • 1970-01-01
    • 2013-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多