有没有办法强制告诉房间 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)。)