【问题标题】:Android Room Multimap issue for the same column names相同列名的Android Room Multimap问题
【发布时间】:2021-12-01 19:24:01
【问题描述】:

official documentation 中所述,Android Room 数据库最好使用 Multimap 返回类型。
在下一个非常简单的示例中,它无法正常工作!

@Entity
data class User(@PrimaryKey(autoGenerate = true) val _id: Long = 0, val name: String)

@Entity
data class Book(@PrimaryKey(autoGenerate = true) val _id: Long = 0, val bookName: String, val userId: Long)

(我相信很多开发人员的表中有 _id 主键)

现在,在 Dao 类中:

@Query(
    "SELECT * FROM user " +
        "JOIN book ON user._id = book.userId"
)
fun allUserBooks(): Flow<Map<User, List<Book>>>

数据库表:

最后,当我运行上述查询时,得到的结果如下:

虽然它应该有 2 个条目,因为对应的表中有 2 个用户。

PS。我现在使用的是最新的 Room 版本,版本 2.4.0-beta02

PPS。问题在于如何生成 UserDao_Impl.java:
所有_id 列都有相同的索引。

这里有机会做点什么吗? (而不是切换到中间数据类)。

【问题讨论】:

    标签: android-room multimap


    【解决方案1】:

    所有 _id 列都有相同的索引。 有机会在这里做点什么吗?

    是的,使用唯一的列名,例如

    @Entity
    data class User(@PrimaryKey(autoGenerate = true) val userid: Long = 0, val name: String)
    
    @Entity
    data class Book(@PrimaryKey(autoGenerate = true) valbookid: Long = 0, val bookName: String, val useridmap: Long) 
    
    • 如下例所示。

    @Entity
    data class User(@PrimaryKey(autoGenerate = true) @ColumnInfo(name="userid")val _id: Long = 0, val name: String)
    
    @Entity
    data class Book(@PrimaryKey(autoGenerate = true) @ColumnInfo(name="bookid")val _id: Long = 0, val bookName: String, val @ColumnInfo(name="userid_map")userId: Long)
    

    否则,您可能已经注意到,Room 使用最后找到的具有重复名称的列的值,而 User 的 _id 是 Book 的 _id 列的值。

    使用上述方法并使用以下方法复制您的数据:-

        db = TheDatabase.getInstance(this)
        dao = db.getAllDao()
    
        var currentUserId = dao.insert(User(name = "Eugene"))
        dao.insert(Book(bookName = "Eugene's book #1", useridmap = currentUserId))
        dao.insert(Book(bookName = "Eugene's book #2", useridmap = currentUserId))
        dao.insert(Book(bookName = "Eugene's book #3", useridmap = currentUserId))
        currentUserId = dao.insert(User(name = "notEugene"))
        dao.insert(Book(bookName = "not Eugene's book #4", useridmap = currentUserId))
        dao.insert(Book(bookName = "not Eugene's book #5", useridmap = currentUserId))
    
        var mapping = dao.allUserBooks() //<<<<<<<<<< BREAKPOINT HERE
        for(m: Map.Entry<User,List<Book>> in mapping) {
    
        }
    
    • 为了方便和简洁,Flow 没有被使用,以上是在主线程上运行的。

    那么结果就是我相信你所期待的:-

    附加

    如果我们已经有了包含很多“_id”字段的数据库结构怎么办?

    那么你需要做出一些决定。

    你可以

    • 进行迁移以重命名列,以避免列名不明确/重复。
    • 使用替代 POJO 并相应地更改提取输出列名称

    例如有:-

    data class Alt_User(val userId: Long, val name: String)
    

    data class Alt_Book (val bookId: Long, val bookName: String, val user_id: Long)
    

    还有:-

    @Query("SELECT user._id AS userId, user.name, book._id AS bookId, bookName, user_id  " +
            "FROM user JOIN book ON user._id = book.user_id")
    fun allUserBooksAlt(): Map<Alt_User, List<Alt_Book>>
    
    • 因此 user._id 以 Alt_User POJO 的名称输出
    • 其他列专门输出(尽管您可以根据 allUserBookAlt2 使用 *)

    :-

    @Query("SELECT *, user._id AS userId, book._id AS bookId " +
            "FROM user JOIN book ON user._id = book.user_id")
    fun allUserBooksAlt2(): Map<Alt_User, List<Alt_Book>>
    
    • 与 allUserBooksAlt 相同,但也有额外的列
    • 你会收到警告warning: The query returns some columns [_id, _id] which are not used by any of [a.a.so70190116kotlinroomambiguouscolumnsfromdocs.Alt_User, a.a.so70190116kotlinroomambiguouscolumnsfromdocs.Alt_Book]. You can use @ColumnInfo annotation on the fields to specify the mapping. You can annotate the method with @RewriteQueriesToDropUnusedColumns to direct Room to rewrite your query to avoid fetching unused columns. You can suppress this warning by annotating the method with @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH). Columns returned by the query: _id, name, _id, bookName, user_id, userId, bookId. public abstract java.util.Map&lt;a.a.so70190116kotlinroomambiguouscolumnsfromdocs.Alt_User, java.util.List&lt;a.a.so70190116kotlinroomambiguouscolumnsfromdocs.Alt_Book&gt;&gt; allUserBooksAlt2();
      • 由于 请注意,如果 Room 有多个同名的列,它不会重写查询,因为它还没有办法区分哪一个是必要的。@RewriteQueriesToDropUnusedColumns 没有不要取消警告。

    如果使用:-

        var mapping = dao.allUserBooksAlt() //<<<<<<<<<< BREAKPOINT HERE
        for(m: Map.Entry<Alt_User,List<Alt_Book>> in mapping) {
        }
    

    会导致:-

    • 可能还有其他选项。

    但是,我建议一劳永逸地解决此问题,方法是使用迁移将列重命名为所有列都具有唯一名称。例如

    【讨论】:

    • 是的,它成功了,谢谢。但是我希望 Room 可以通过自己的方式做到这一点。
    • 如果我们已经有了包含很多“_id”字段的数据库结构怎么办?
    • @GoltsevEugene 更新了答案,但可能不是好消息。
    • 有类似的想法。非常感谢,尤其是 @Rewrite 的事情,也许有一天会有所帮助!
    猜你喜欢
    • 1970-01-01
    • 2017-11-05
    • 2018-02-05
    • 2022-08-10
    • 2018-03-21
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    • 2021-04-04
    相关资源
    最近更新 更多