【问题标题】:RawQuery cannot compile: "Cannot figure out how to read this field from a cursor."RawQuery 无法编译:“无法弄清楚如何从游标中读取此字段。”
【发布时间】:2022-06-11 01:09:52
【问题描述】:

我的项目中有多个@RawQuerys,自定义data classes 作为返回类型,到目前为止效果很好,但现在我试图拥有一个data classList<OtherResult> 属性和这个在编译时失败:

error: Cannot figure out how to read this field from a cursor.

所以基本上:

data class CustomResult(
    val count: Int,
    val name: String,
    val values: List<SubResult>,
)
data class SubResult(
    val sub_name: String,
    val sub_code: String,
)

------
Dao:

@RawQuery
abstract fun getCustomResultRaw(query: SimpleSQLiteQuery): List<CustomResult>

fun getCustomResult(): List<CustomResult> {
   val params = emptyArray<Any>()
   val query = "SELECT ..... "
   return getCustomResultRaw(SimpleSQLiteQuery(query, params))
}

有没有办法强制告诉房间List&lt;&gt; 属性应该被解析为SubResult?我还有哪些其他选择?

【问题讨论】:

    标签: android-room


    【解决方案1】:

    有没有办法强制告诉房间 List 属性应该被解析为 SubResult?

    不,不是那样的。 Room 将数据存储在 SQLite 表中,从 SQLite 的角度来看,这些表由列和一组有限的类型组成:-

    • INTEGER(64 位有符号整数,例如 Long、Int、Byte ...)
    • TEXT(字符串,例如字符串)
    • REAL(8 字节 IEEE 浮点数,例如 Double、Float ...)
    • BLOB(字节流,例如 ByteArray)
    • NUMERIC(以上任何一种但 ROOM 不支持

    一列不能直接包含多个值,例如列表。

    我还有什么其他选择?

    基本上有两种选择:-

    • SubResult 值存储在与 CustomResult 有关系的另一个表中,从关系数据库的角度来看,这是一种方式。
      • CustomResult 将是父级,SubResult 将是子级(子级)
    • SubResults 存储为允许的类型之一(可能排除 INTEGER 或 REAL,排除 NULL),因此您需要将该数据转换为 ByteArray 或 String。通常使用后者,字符串是数据的 JSON 表示。然后,您需要定义 TypeConverters 以让 ROOM 知道如何处理转换。

    工作示例

    以下是使用这两种方法的示例。

    注意为了满足 SubResults 列表的转换,Google Gson 库添加了依赖项implementation 'com.google.code.gson:gson:2.9.0' (如果使用带有相关 SubResults 的第二个表,则不需要这样做)

    这是数据类:-

    data class CustomResult(
        val count: Int,
        val name: String,
        /*val values: List<SubResult>, uses SubResultList to cater for Type Converters*/
        val values: SubResultList /* Not required if using table for SubResults */
    )
    data class SubResult(
        val sub_name: String,
        val sub_code: String,
    )
    /* Holder for List to suit conversion of SubResult List - Not needed if using table for SubResults*/
    data class SubResultList(
        val srList: List<SubResult>
    )
    /* Primary Table for the CR's (as well as the converted list of SubResults)*/
    @Entity
    data class CRTable(
        @PrimaryKey
        val crId: Long?=null, /* Uniquely Identifies the CR Row - generated if null */
        @Embedded
        val customResult: CustomResult
    )
    /* Second table for the SR's - not needed if using JSON representation of SR List*/
    @Entity
    data class SRTable(
        @PrimaryKey
        val srId: Long?=null,
        @ColumnInfo(index = true)
        val parentCrId: Long,
        @Embedded
        val subResult: SubResult
    )
    /* For retrieving the CRTables WITH the related SRTable rows - not needed if using JSON representation of SR List*/
    data class CRTableWithRelatedSRTables(
        @Embedded
        val crTable: CRTable,
        @Relation(entity = SRTable::class, parentColumn = "crId", entityColumn = "parentCrId")
        val srTableList: List<SRTable>
    )
    

    转换器 (2) 将 SubResultList 转换为 JSON 并返回:-

    class Converters {
        @TypeConverter
        fun convertSubResultListToJSONString(subResultList: SubResultList): String = Gson().toJson(subResultList)
        @TypeConverter
        fun convertJSONStringToSubResultList(jsonString: String): SubResultList = Gson().fromJson(jsonString,SubResultList::class.java)
    
    }
    

    DAO 接口(访问数据库的函数):-

    @Dao
    interface AllDao {
    
        @Insert(onConflict = OnConflictStrategy.IGNORE)
        fun insert(crTable: CRTable): Long
        
        /* Not Needed if storing Subresults as an embedded list */
        @Insert(onConflict = OnConflictStrategy.IGNORE)
        fun insert(srTable: SRTable): Long
    
        /* Query for embedded Subresults list */
        @Query("SELECT * FROM crtable")
        fun getAllCRTableRows(): List<CRTable>
    
        /* Query for both embedded Subresults (as they are combned in this example) and the related SRTable rows */
        @Transaction
        @Query("SELECT * FROM crtable")
        fun getAllCRTablesWithRelatedSRTables(): List<CRTableWithRelatedSRTables>
    }
    

    一个带有 @Database 注释的类,实例有一个单例。注意为了简洁和方便,允许在主线程上运行。

    @TypeConverters(value = [Converters::class])
    @Database(entities = [CRTable::class,SRTable::class], version = 1, exportSchema = false)
    abstract class TheDatabase: RoomDatabase() {
        abstract fun getAllDao(): AllDao
    
        companion object {
            private var instance: TheDatabase? = null
            fun getInstance(context: Context): TheDatabase {
                if (instance==null) {
                    instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
                        .allowMainThreadQueries()
                        .build()
                }
                return instance as TheDatabase
            }
        }
    }
    
    • 如果只使用这两个表,则不需要定义 @TypConverters。
    • 如果只是嵌入 SubResultsList,那么 SRTable 类将不会包含在实体参数中

    最后把它们放在一个活动中:-

    const val TAG = "DBINFO"
    class MainActivity : AppCompatActivity() {
    
        lateinit var db: TheDatabase
        lateinit var dao: AllDao
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            db = TheDatabase.getInstance(this)
            dao = db.getAllDao()
            
            /* Preapre some SubResults */
            val sr01 = SubResult("SR01","CODEA")
            val sr02 = SubResult("SR02","CODEB")
            val sr03 = SubResult("SR03","CODEC")
            val sr04 = SubResult("SR04","CODED")
            val sr05 = SubResult("SR05","CODEE")
    
            /* Prepare some SubResultLists */
            val srl1 = SubResultList(listOf(sr01,sr02,sr03))
            val srl2 = SubResultList(listOf(sr04,sr05))
            val srl3 = SubResultList(listOf(sr01,sr02,sr03,sr04,sr05))
    
            /* Add CustomResults for embedded SubresultList (i.e. converted to JSON)*/
            val cr01 = dao.insert(CRTable(customResult =  CustomResult(1,"CR01",srl1)))
            val cr02 = dao.insert(CRTable(customResult = CustomResult(2,"CR02",srl2)))
            val cr03 = dao.insert(CRTable(customResult = CustomResult(3,"CR03",srl3)))
            
            /* Add the related SRTable rows (each block is per parent CustomResult) */
    
            dao.insert(SRTable(null,cr01,sr01))
            dao.insert(SRTable(null,cr01,sr02))
            dao.insert(SRTable(null,cr01,sr03))
    
            dao.insert(SRTable(null,cr02,sr04))
            dao.insert(SRTable(null,cr02,sr05))
    
            dao.insert(SRTable(null,cr03,sr01))
            dao.insert(SRTable(null,cr03,sr02))
            dao.insert(SRTable(null,cr03,sr03))
            dao.insert(SRTable(null,cr03,sr04))
            dao.insert(SRTable(null,cr03,sr05))
    
            /* Extract and inspect the data (only need the more complex query as CRTable has the embedded SubResultsList) */
            for (crwsr in dao.getAllCRTablesWithRelatedSRTables()) {
                /* For each CRTable row */
                Log.d(TAG,"CRTable is ${crwsr.crTable.customResult.name} count is ${crwsr.crTable.customResult.count} it has ${crwsr.crTable.customResult.values.srList.size} SR's, it also has ${crwsr.srTableList.size} related SRTable rows.")
                Log.d(TAG,"SR's are:-")
                /* For each item in the SubResultList (for the current CustomResult) */
                for (sr in crwsr.crTable.customResult.values.srList) {
                    Log.d(TAG,"\tSR is ${sr.sub_name} code is ${sr.sub_code}")
                }
                Log.d(TAG,"Related SR's are:-")
                /* For each related row in the SRTable (for the current CustomResult) */
                for (srt in crwsr.srTableList) {
                    Log.d(TAG,"\tSR is ${srt.subResult.sub_name} code is ${srt.subResult.sub_code} ID is ${srt.srId} related to (child of) CR with an ID of ${srt.parentCrId}")
                }
            }
        }
    }
    

    结果

    日志包括:-

    2022-06-10 05:54:48.982 D/DBINFO: CRTable is CR01 count is 1 it has 3 SR's, it also has 3 related SRTable rows.
    2022-06-10 05:54:48.982 D/DBINFO: SR's are:-
    2022-06-10 05:54:48.982 D/DBINFO:   SR is SR01 code is CODEA
    2022-06-10 05:54:48.982 D/DBINFO:   SR is SR02 code is CODEB
    2022-06-10 05:54:48.982 D/DBINFO:   SR is SR03 code is CODEC
    2022-06-10 05:54:48.982 D/DBINFO: Related SR's are:-
    2022-06-10 05:54:48.982 D/DBINFO:   SR is SR01 code is CODEA ID is 1 related to (child of) CR with an ID of 1
    2022-06-10 05:54:48.982 D/DBINFO:   SR is SR02 code is CODEB ID is 2 related to (child of) CR with an ID of 1
    2022-06-10 05:54:48.982 D/DBINFO:   SR is SR03 code is CODEC ID is 3 related to (child of) CR with an ID of 1
    2022-06-10 05:54:48.983 D/DBINFO: CRTable is CR02 count is 2 it has 2 SR's, it also has 2 related SRTable rows.
    2022-06-10 05:54:48.983 D/DBINFO: SR's are:-
    2022-06-10 05:54:48.983 D/DBINFO:   SR is SR04 code is CODED
    2022-06-10 05:54:48.983 D/DBINFO:   SR is SR05 code is CODEE
    2022-06-10 05:54:48.983 D/DBINFO: Related SR's are:-
    2022-06-10 05:54:48.983 D/DBINFO:   SR is SR04 code is CODED ID is 4 related to (child of) CR with an ID of 2
    2022-06-10 05:54:48.983 D/DBINFO:   SR is SR05 code is CODEE ID is 5 related to (child of) CR with an ID of 2
    2022-06-10 05:54:48.983 D/DBINFO: CRTable is CR03 count is 3 it has 5 SR's, it also has 5 related SRTable rows.
    2022-06-10 05:54:48.983 D/DBINFO: SR's are:-
    2022-06-10 05:54:48.983 D/DBINFO:   SR is SR01 code is CODEA
    2022-06-10 05:54:48.983 D/DBINFO:   SR is SR02 code is CODEB
    2022-06-10 05:54:48.983 D/DBINFO:   SR is SR03 code is CODEC
    2022-06-10 05:54:48.984 D/DBINFO:   SR is SR04 code is CODED
    2022-06-10 05:54:48.984 D/DBINFO:   SR is SR05 code is CODEE
    2022-06-10 05:54:48.984 D/DBINFO: Related SR's are:-
    2022-06-10 05:54:48.984 D/DBINFO:   SR is SR01 code is CODEA ID is 6 related to (child of) CR with an ID of 3
    2022-06-10 05:54:48.984 D/DBINFO:   SR is SR02 code is CODEB ID is 7 related to (child of) CR with an ID of 3
    2022-06-10 05:54:48.984 D/DBINFO:   SR is SR03 code is CODEC ID is 8 related to (child of) CR with an ID of 3
    2022-06-10 05:54:48.984 D/DBINFO:   SR is SR04 code is CODED ID is 9 related to (child of) CR with an ID of 3
    2022-06-10 05:54:48.984 D/DBINFO:   SR is SR05 code is CODEE ID is 10 related to (child of) CR with an ID of 3
    
    • 即预期结果(第一个 CR 有 3 个 SR,第二个 CR 有 2 个 SR,第三个 CR 有 5 个 SR两种方法

    实际存储的数据:-

    CRTable 有 3 行:-

    • 可以看出,values 具有 SubResults 列表(作为 SubResultsList)。缺点是查询数据,例如,如果您只想要具有特定 SR 代码的 CR,您不能只说 WHERE values = 'CODEE',您可能会使用 WHERE values LIKE '%CODEE%' (但那是效率低下,因为它需要全表扫描)。同样可以看出,由于 JSON 语法和命名,存储了大量 BLOAT。

    SRTable 每个相关的 SR 都有一行,所以 10 行:-

    • 存储的数据少得多
    • 搜索可能是通过索引(SQLite 查询优化器会做它的业务)
    • SELECT SQL 可能更复杂(尤其是当 Room 的便利性不合适时,例如,示例中的查询使用 Room 构建的子查询来获取相关 SR 的行,并且与 ALL 相关(因此是 @Transaction)。)

    【讨论】:

      【解决方案2】:

      我无法解决我的问题,但找到了使用 multimap 的解决方法,如in the documentation 所述,即。在这种情况下返回 Map&lt;CustomResult, List&lt;SubResult&gt;&gt;:

      持有List&lt;&gt; 的主类如下:

      data class CustomResult(
          val count: Int,
          val name: String,
      ) {
          @Ignore
          var values: List<SubResult> = emptyList() // note: `var` instead of `val`
      }
      

      道的功能然后是这样的:

      // now returning a Map<>
      @RawQuery
      abstract fun getCustomResultRaw(query: SimpleSQLiteQuery): Map<CustomResult, List<SubResult>>
      
      // maps the Map<> to a List<> 
      fun getCustomResult(): List<CustomResult> {
          val params = emptyArray<Any>()
          val query = "SELECT ..... "
          val resultMap = getCustomResultRaw(SimpleSQLiteQuery(query, params))
          return resultMap.map { e ->
              CustomResult(
                  count = e.key.count,
                  name = e.key.name,
              ).apply {
                  values = e.value
              }
          }
      

      }

      【讨论】:

        猜你喜欢
        • 2017-11-03
        • 1970-01-01
        • 2014-12-20
        • 2013-04-10
        • 1970-01-01
        • 2014-12-18
        • 1970-01-01
        • 1970-01-01
        • 2015-10-26
        相关资源
        最近更新 更多