Android Room 多重映射问题,涉及相同列名。

9

正如官方文档所述,对于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>>>

数据库表如下:
enter image description here enter image description here 最后,当我运行上述查询时,我得到的结果如下:
enter image description here 尽管应该有两个条目,因为相应的表中有两个用户。
注:我在这一点上使用的是最新的 Room 版本,即 Version 2.4.0-beta02。
附言:问题在于 UserDao_Impl.java 的生成方式:
enter image description here所有的_id列在那里都具有相同的索引。
有办法解决这个问题吗?(而不是切换到中间数据类)。
1个回答

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) 
  • 如下面的示例所示。

或者

@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使用具有重复名称的最后找到的列的值,并且用户的_id是书籍_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。然后,我相信你所期望的结果是:-

enter image description here

补充

如果我们已经有了带有许多“_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>>
  • 因此,根据Alt_User POJO,输出使用者的_id和名称
  • 特定地输出其他列(尽管你可以像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<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) {
    }

这将导致: enter image description here 可能还有其他选项。
然而,我建议通过迁移来解决这个问题,将列重命名为唯一名称。例如:

是的,它能解决问题,谢谢。 不过我希望Room可以通过自己的手段来完成。 - Goltsev Eugene
如果我们已经有了许多"_id"字段的数据库结构,该怎么办? - Goltsev Eugene
1
@GoltsevEugene更新了答案,但可能不是好消息。 - MikeT
有相似的想法。非常感谢,特别是那个@Rewrite的东西,将来可能会很有帮助! - Goltsev Eugene
1
问题已经在几天前得到解决。我想它应该会在未来的 Room 版本中提供。 - Nit
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接