Как связать две таблицы в Room
Что проверяют
Понимаете ли вы, что Room намеренно не поддерживает ссылки между объектами с ленивой загрузкой, как классические ORM, и связь собирается явно. Следом обычно спрашивают про связь многие ко многим и зачем над методом @Transaction.
Как отвечать
Разделите две вещи: ограничение в схеме и сборку связанных объектов.
Ограничение задаётся внешним ключом в дочерней сущности: @Entity(foreignKeys = [ForeignKey(entity = User::class, parentColumns = ["userId"], childColumns = ["userId"], onDelete = ForeignKey.CASCADE)]). Колонку userId в Order стоит проиндексировать через indices, иначе Room выдаст предупреждение при компиляции, а изменение родителя будет сканировать всю дочернюю таблицу.
Сборка объектов:
- один к одному и один ко многим: класс-обёртка с
@Embedded val user: Userи@Relation(parentColumn = "userId", entityColumn = "userId") val orders: List<Order>; - многие ко многим: отдельная сущность связи, например
UserGroupCrossRefс составным первичным ключом, и@Relationс параметромassociateBy = Junction(UserGroupCrossRef::class); @Transactionна методе DAO: для такой обёртки Room выполняет несколько запросов, сначала родителей, потом детей. Без транзакции между ними может пройти запись, и данные окажутся несогласованными;- JOIN без обёртки: запрос с JOIN может вернуть
Map<User, List<Order>>, это удобно, когда нужен фильтр по дочерней таблице.
Метод делают suspend или возвращают Flow: запросы в главном потоке Room по умолчанию запрещает.
Внешний ключ меняет схему: нужны новая версия и миграция, пересоздающая таблицу, потому что ALTER TABLE в SQLite не добавляет ограничение к существующей колонке.
Где спотыкаются
Считают, что @Relation создаёт связь в базе. Это только инструкция для генерации кода, а целостность и каскадное удаление даёт ForeignKey.
Забывают @Transaction. Ошибка не проявляется в тестах и всплывает редкими рассинхронами под нагрузкой.
Добавляют внешний ключ, поднимают версию и включают fallbackToDestructiveMigration() вместо миграции. У пользователей база молча стирается при обновлении.