【问题标题】:How to return a Map<String, List<CustomVO>> from single Room entity with Android Room如何使用 Android Room 从单个 Room 实体返回 Map<String, List<CustomVO>>
【发布时间】:2021-11-19 10:08:48
【问题描述】:

我正在调查 Android Rooms 是否支持从查询返回地图

我无法克服这个构建错误

error: The columns returned by the query does not have the fields [columnOne,columnTwo,columnThree,columnFour,columnFive,columnSix,columnSeven,columnEight] in org.my.MyDataVO even though they are annotated as non-null or primitive. Columns returned by the query: [columnA,columnB]
    public abstract java.util.Map<java.lang.String, java.util.List<org.my.MyDataVO>> fetchMap(@org.jetbrains.annotations.NotNull()

我的 DAO 查询类似于:-

@MapInfo(keyColumn = "column_a", valueColumn = "column_b")
@Query("SELECT column_a, column_b FROM my_data_table WHERE my_key_column = :myKeyColumn")
fun fetchMap(myKeyColumn: String): Map<String, List<org.my.MyDataVO>>

我的 DO 对象类似于:-

@Entity(
    tableName = "my_data_table",
    indices = [
        Index(value = ["key_column"], unique = false),
    ]
)
@TypeConverters(MyDataVOListTypeConverter::class)
data class BookTableContentChildrenDO(
    @ColumnInfo(name = "key_column") val keyColumn: String,
    @ColumnInfo(name = "column_a") val columnA: String,
    @ColumnInfo(name = "column_b") val columnB: List<org.my.MyDataVO>
) {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "my_data_table_local_id")
    var myDataTableLocalId: Long = 0L
}

我的值对象 MyDataVO 类似于:-

@Serializable
data class MyDataVO(
    @ColumnInfo(name = "column_one") val columnOne: Long,
    @ColumnInfo(name = "column_two") val columnTwo: String,
    @ColumnInfo(name = "column_three") val columnThree: Boolean,
    @ColumnInfo(name = "column_four") val columnFour: String,
    @ColumnInfo(name = "column_five") val columnFive: String,
    @ColumnInfo(name = "column_six") val columnSix: String,
    @ColumnInfo(name = "column_seven") val columnSeven: String,
    @ColumnInfo(name = "column_eight") val columnEight: Long,
)

我的尝试是不可能的吗?

我在哪里犯错了?

为什么不能将房间映射my_data_table.column_b 到List&lt;org.my.MyDataVO&gt;

更新

我设法通过将我所有的 TypeConverters 添加到 @Database 抽象类定义来解决构建错误

但是我现在面临一个看起来很奇怪的运行时错误

我有org.my.MyDataVO 和List&lt;org.my.MyDataVO&gt; 的单个实例的类型转换器,Room 使用单个实例类型转换器来解码List&lt;org.my.MyDataVO&gt;

当我尝试调用我的 DAO fetchMap() 函数时,它失败了

java.util.concurrent.ExecutionException: kotlinx.serialization.json.internal.JsonDecodingException: Expected start of the object '{', but had 'EOF' instead
    JSON input: .....xxx","columnSix":1,"columnSeven":1,"columnEight":""}]}]

为什么 Room 使用了错误的 TypeConverter?

如果我没有在我的 @Database 抽象类中提到 Single 实例 TypeConverter,我会收到构建错误。

更新 2

如果我将 DAO fetchMap 方法返回类型更改为 Map&lt;String, List&lt;Any&gt;&gt;,我可以让它工作

现在 Room 使用了正确的类型转换器,我可以正常获取地图

更新 3 经过进一步的测试,我发现虽然我没有遇到任何崩溃或构建问题,但我的 DAO 没有按我的要求工作。虽然我插入了多个org.my.MyDataVO 的条目列表,但我的 fetchMap 函数返回的列表只包含一个条目。为什么 Room 不能返回存储在 DB 表中的完整列表?

【问题讨论】:

    标签: android android-room


    【解决方案1】:

    为什么 Room 不能返回存储在 DB 表中的完整列表?

    简而言之,您不能将列直接作为列表/数组。

    所以@ColumnInfo(name = "column_b") val columnB: List&lt;org.my.MyDataVO&gt; 是行不通的。

    添加一个POJO比如

    data class MyDataVOListHolder(
        val myDataVOListHolder: List<org.my.MyDataVO>
    )
    

    然后使用 POJO 作为列类型,例如

    @ColumnInfo(name = "column_b") val columnB: MyDataVOListHolder
    

    显然,您需要合适的类型转换器来将 myDataVOListHolder 与 Room 可以处理的类型(例如 JSON 字符串)相互转换。

    举个例子:-

    lateinit var db: TheDatabase
    lateinit var dao: BookTableContentChildrenDODao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    
        db = TheDatabase.getInstance(this)
        dao = db.getDao()
    
        dao.insert(
            BookTableContentChildrenDO(keyColumn = "AAA", columnA = "Blah", columnB =  MyDataVOListHolder(myDataVOListHolder = createMyDataVOList()))
        )
        dao.insert(
            BookTableContentChildrenDO(keyColumn = "BBB", columnA = "Blah", columnB =  MyDataVOListHolder(myDataVOListHolder = createMyDataVOList()))
        )
        dao.insert(
            BookTableContentChildrenDO(keyColumn = "CCC", columnA = "Blah", columnB =  MyDataVOListHolder(myDataVOListHolder = createMyDataVOList()))
        )
    
        for(m in dao.fetchMap("BBB")) {
            Log.d("DBINFO","Key is ${m.key}")
            for (myDataVo: MyDataVO in m.value.myDataVOListHolder) {
                Log.d("DBINFO-EXTRA","\t C1 = ${myDataVo.columnOne} C2 = ${myDataVo.columnTwo} C3 = ${myDataVo.columnThree} etc...."
                )
            }
        }
    }
    
    fun createMyDataVOList(): List<MyDataVO> {
        var myDataVoList = ArrayList<MyDataVO>()
        for (i in 1 until 5) {
            myDataVoList.add(
                MyDataVO((i * 3).toLong(),"col2_" +i.toString(),i % 2 == 0,"col4","col5","col6","col7",i.toLong())
            )
        }
        return myDataVoList
    }
    

    导致日志包含:-

    D/DBINFO: Key is Blah
    D/DBINFO-EXTRA:      C1 = 3 C2 = col2_1 C3 = false etc....
    D/DBINFO-EXTRA:      C1 = 6 C2 = col2_2 C3 = true etc....
    D/DBINFO-EXTRA:      C1 = 9 C2 = col2_3 C3 = false etc....
    D/DBINFO-EXTRA:      C1 = 12 C2 = col2_4 C3 = true etc....
    

    【讨论】:

    • 我已经有了那些类型转换器
    • @Hector 太棒了,你知道我将要转换的类命名为 MyDataVOListHolder。附言根据我的答案中的代码添加了一个工作示例(直接接受使用 MyDataVO 而不是从其他地方)
    猜你喜欢
    • 2017-11-23
    • 1970-01-01
    • 1970-01-01
    • 2019-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    相关资源
    最近更新 更多