Проблема с несколькими картами Android для одних и тех же имен столбцов

#android-room #multimap

#android-room #multimap

Вопрос:

Как указано в официальной документации, предпочтительнее использовать возвращаемый тип Multimap для базы данных Android Room.
В следующем очень простом примере это работает некорректно!

 @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 столбцы имеют там один и тот же индекс.

Есть ли шанс что-то сделать здесь? (вместо переключения на промежуточные классы данных).

Ответ №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 использует значение последнего найденного столбца с дублированным именем, а идентификатор пользователя — это значение столбца _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) {

    }
 
  • для удобства и краткости a 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>>
 
  • итак, пользователь._id выводится с именем в соответствии с POJO Alt_User
  • другие столбцы выводятся специально (хотя вы могли бы использовать * в соответствии с 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) {
    }
 

Приведет к :-

введите описание изображения здесь

  • возможно, другие варианты.

Тем не менее, я бы предложил устранить проблему раз и навсегда, используя миграцию для переименования столбцов, чтобы все они имели уникальные имена. например

Комментарии:

1. Да, это помогает, спасибо. Однако я надеялся, что Room сможет сделать это как-то своими силами.

2. Что, если у нас уже есть структура базы данных с большим количеством полей «_id»?

3. @GoltsevEugene обновил ответ, но, вероятно, не очень хорошие новости.

4. Есть похожие мысли. Большое спасибо и особенно за эту вещь @Rewrite, может быть, когда-нибудь пригодится!

5. Проблема была исправлена несколько дней назад. Я полагаю, это должно быть доступно в будущей версии Room. issuetracker.google.com/issues/201306012