Поскольку SQLite — это реляционная база данных, вы можете определять отношения между сущностями. Но хотя большинство библиотек объектно-реляционного сопоставления позволяют объектам сущностей ссылаться друг на друга, Room явно запрещает это. Чтобы узнать о технических причинах этого решения, см. раздел «Понимание того, почему Room не разрешает ссылки на объекты» .
Типы отношений
Комната поддерживает следующие типы отношений:
- Один-к-одному : представляет связь, в которой один объект связан с другим отдельным объектом.
 - Один-ко-многим : представляет связь, при которой один объект может быть связан с несколькими объектами другого типа.
 - Многие-ко-многим : представляет связь, при которой несколько сущностей одного типа могут быть связаны с несколькими сущностями другого типа. Обычно для этого требуется соединительная таблица.
 -  Вложенные отношения (с использованием встроенных объектов) : представляют связь, в которой сущность содержит другую сущность в качестве поля, и эта вложенная сущность может дополнительно содержать другие сущности. При этом используется аннотация 
@Embedded. 
Выбирайте между двумя подходами
В Room есть два способа определить и запросить связь между сущностями. Вы можете использовать либо:
- Промежуточный класс данных со встроенными объектами или
 - Метод реляционного запроса с возвращаемым типом multimap.
 
Если у вас нет конкретной причины использовать промежуточные классы данных, мы рекомендуем использовать подход типа возвращаемого значения multimap. Дополнительные сведения об этом подходе см. в разделе Возврат мультикарты .
Подход с промежуточным классом данных позволяет избежать написания сложных SQL-запросов, но он также может привести к увеличению сложности кода, поскольку требует дополнительных классов данных. Короче говоря, подход типа возвращаемого значения с несколькими отображениями требует, чтобы ваши SQL-запросы выполняли больше работы, а подход с промежуточным классом данных требует, чтобы ваш код выполнял больше работы.
Используйте подход промежуточного класса данных
При подходе с промежуточным классом данных вы определяете класс данных, который моделирует отношения между объектами вашей комнаты. Этот класс данных содержит пары между экземплярами одного объекта и экземплярами другого объекта в виде встроенных объектов . Ваши методы запроса могут затем возвращать экземпляры этого класса данных для использования в вашем приложении.
 Например, вы можете определить класс данных UserBook для представления пользователей библиотеки с определенными извлеченными книгами, а также определить метод запроса для получения списка экземпляров UserBook из базы данных: 
Котлин
@Dao
interface UserBookDao {
    @Query(
        "SELECT user.name AS userName, book.name AS bookName " +
        "FROM user, book " +
        "WHERE user.id = book.user_id"
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>
}
data class UserBook(val userName: String?, val bookName: String?)
Ява
@Dao
public interface UserBookDao {
   @Query("SELECT user.name AS userName, book.name AS bookName " +
          "FROM user, book " +
          "WHERE user.id = book.user_id")
   public LiveData<List<UserBook>> loadUserAndBookNames();
}
public class UserBook {
    public String userName;
    public String bookName;
}
Используйте подход типов возврата с несколькими отображениями
В подходе типа возвращаемого значения с несколькими отображениями вам не нужно определять какие-либо дополнительные классы данных. Вместо этого вы определяете тип возвращаемого значения multimap для своего метода на основе желаемой структуры карты и определяете связь между вашими сущностями непосредственно в SQL-запросе.
 Например, следующий метод запроса возвращает сопоставление экземпляров User и Book для представления пользователей библиотеки с определенными извлеченными книгами: 
Котлин
@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id"
)
fun loadUserAndBookNames(): Map<User, List<Book>>
Ява
@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id"
)
public Map<User, List<Book>> loadUserAndBookNames();
Создание встроенных объектов
 Иногда вам нужно выразить сущность или объект данных как единое целое в логике вашей базы данных, даже если объект содержит несколько полей. В таких ситуациях вы можете использовать аннотацию @Embedded для представления объекта, который вы хотите разложить на подполя в таблице. Затем вы можете запросить встроенные поля так же, как и для других отдельных столбцов.
 Например, ваш класс User может включать поле типа Address , которое представляет собой комбинацию полей с именами street , city , state и postCode . Чтобы хранить составные столбцы в таблице отдельно, включите поле Address . Это должно появиться в классе User с аннотацией @Embedded . Следующий фрагмент кода демонстрирует это: 
Котлин
data class Address(
    val street: String?,
    val state: String?,
    val city: String?,
    @ColumnInfo(name = "post_code") val postCode: Int
)
@Entity
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    @Embedded val address: Address?
)
Ява
public class Address {
    public String street;
    public String state;
    public String city;
    @ColumnInfo(name = "post_code") public int postCode;
}
@Entity
public class User {
    @PrimaryKey public int id;
    public String firstName;
    @Embedded public Address address;
}
 Таблица, представляющая объект User , затем содержит столбцы со следующими именами: id , firstName , street , state , city и post_code .
 Если сущность имеет несколько встроенных полей одного типа, вы можете сохранить уникальность каждого столбца, задав свойство prefix . Затем Room добавляет предоставленное значение в начало имени каждого столбца во внедренном объекте.
Дополнительные ресурсы
Чтобы узнать больше об определении связей между сущностями в Room, см. следующие дополнительные ресурсы.
Видео
- Что нового в Room (Android Dev Summit '19)