Android Room Multimap 相同列名称的问题

Gol*_*ene 9 multimap android-room

正如官方文档中所述,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)
Run Code Online (Sandbox Code Playgroud)

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

现在,在 Dao 类中:

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

数据库表:
在此输入图像描述

在此输入图像描述

最后,当我运行上述查询时,我得到的结果如下: 在此输入图像描述

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

附言。我目前使用的是最新的 Room 版本,版本 2.4.0-beta02

聚苯硫醚。问题在于 UserDao_Impl.java 的生成方式: 在此输入图像描述
所有_id列都有相同的索引。

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

Mik*_*keT 9

所有 _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) 
Run Code Online (Sandbox Code Playgroud)
  • 如下例所示。

或者

@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)
Run Code Online (Sandbox Code Playgroud)

否则,您可能已经注意到,Room 使用最后找到的具有重复名称的列的值,而用户的 _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) {

    }
Run Code Online (Sandbox Code Playgroud)
  • 为了方便和简洁,Flow没有使用 a ,上面的代码在主线程上运行。

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

在此输入图像描述

额外的

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

然后你需要做出一些决定。

你可以

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

例如有:-

data class Alt_User(val userId: Long, val name: String)
Run Code Online (Sandbox Code Playgroud)

data class Alt_Book (val bookId: Long, val bookName: String, val user_id: Long)
Run Code Online (Sandbox Code Playgroud)

随着 :-

@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>>
Run Code Online (Sandbox Code Playgroud)
  • 因此 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>>
Run Code Online (Sandbox Code Playgroud)
  • 与 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<a.a.so70190116kotlinroomambiguouscolumnsfromdocs.Alt_User, java.util.List<a.a.so70190116kotlinroomambiguouscolumnsfromdocs.Alt_Book>> allUserBooksAlt2();
    • 由于注意,如果 Room 具有多个同名列,则它不会重写查询,因为它还没有办法区分哪一列是必需的。@RewriteQueriesToDropUnusedColumns并没有消除警告。

如果使用:-

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

会导致:-

在此输入图像描述

  • 可能有其他选择。

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