Ответ
В Room для связи таблиц используйте аннотации @Relation, @ForeignKey или создание промежуточной таблицы для many-to-many связи.
1:1 или 1:many (с ForeignKey):
@Entity(foreignKeys = [ForeignKey(
entity = User::class,
parentColumns = ["id"],
childColumns = ["userId"],
onDelete = CASCADE
)])
data class Order(
@PrimaryKey val id: Long,
val userId: Long
)
data class UserWithOrders(
@Embedded val user: User,
@Relation(
parentColumn = "id",
entityColumn = "userId"
)
val orders: List<Order>
)
Many-to-many (через junction таблицу):
@Entity
data class Playlist(
@PrimaryKey val id: Long
)
@Entity
data class Song(
@PrimaryKey val id: Long
)
@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
val playlistId: Long,
val songId: Long
) Ответ 18+ 🔞
А, ну вот, опять про эти связи в Room заговорили. Слушай, тут всё просто, как три копейки, но если не понять — потом овердохуища проблем будет. Смотри, у тебя есть таблицы, и их надо как-то связать, чтобы не получилась каша, где всё в одной куче валяется.
Когда у тебя один к одному или один ко многим — тут вообще элементарно. Берёшь ForeignKey, как гвоздь в стену, и прибиваешь одну таблицу к другой. Смотри, вот пример: есть User и Order. Один юзер может иметь много заказов, да? Значит, в таблице заказов делаешь колонку userId, которая ссылается на id в таблице юзеров. Это и есть внешний ключ, ёпта.
@Entity(foreignKeys = [ForeignKey(
entity = User::class,
parentColumns = ["id"],
childColumns = ["userId"],
onDelete = CASCADE // Это чтобы если юзера удалили, заказы его тоже накрылись медным тазом
)])
data class Order(
@PrimaryKey val id: Long,
val userId: Long // Вот эта самая ссылочка
)
А чтобы потом всё это красиво достать одним запросом, а не бегать по циклу, делаешь специальный data class. В него засовываешь (@Embedded) самого юзера, а рядом через @Relation говоришь: "эй, Room, притяни-ка сюда все заказы, где userId совпадает с id этого юзера".
data class UserWithOrders(
@Embedded val user: User,
@Relation(
parentColumn = "id", // id из User (который в @Embedded)
entityColumn = "userId" // userId из Order
)
val orders: List<Order> // И Room сам всё подтянет, красота!
)
А вот many-to-many — это уже посложнее, хитрая жопа. Представь плейлисты и песни. Один плейлист содержит много песен, и одна песня может быть в многих плейлистах. Тут просто так колонку не добавишь, нужна промежуточная таблица-связка. Её ещё junction table или cross-ref называют.
Создаёшь две основные таблицы:
@Entity
data class Playlist(
@PrimaryKey val id: Long
)
@Entity
data class Song(
@PrimaryKey val id: Long
)
А теперь самое главное — таблица-посредник. В ней всего две колонки: playlistId и songId. И primary key делаешь составной из обеих, чтобы одна и та же пара не повторялась.
@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
val playlistId: Long,
val songId: Long
)
Вот и вся магия. Потом в DAO пишешь запрос с JOIN через эту таблицу, и Room тебе вернёт PlaylistWithSongs, где у каждого плейлиста будет список его песен. Главное — не перепутай колонки, а то получится пиздопроебибна, и вместо рок-баллад в плейлист "Для сна" попадёт какой-нибудь хардбасс.