Поскольку SQLite — это реляционная база данных, вы можете определять связи между сущностями. Но в то время как большинство библиотек объектно-реляционного отображения позволяют объектам сущностей ссылаться друг на друга, Room явно это запрещает. Чтобы узнать о технических причинах этого решения, см. раздел «Понимание того, почему Room не разрешает ссылки на объекты» .
Типы отношений
Room поддерживает следующие типы отношений:
- «Один к одному» : обозначает отношение, при котором один объект связан с другим единственным объектом.
- «Один ко многим» : обозначает отношение, при котором один объект может быть связан с несколькими объектами другого типа.
- Связь «многие ко многим» : представляет собой отношение, при котором несколько сущностей одного типа могут быть связаны с несколькими сущностями другого типа. Обычно для этого требуется таблица связей.
- Вложенные связи (с использованием встроенных объектов) : представляют собой связь, в которой одна сущность содержит другую сущность в качестве поля, и эта вложенная сущность может, в свою очередь, содержать другие сущности. Для этого используется аннотация
@Embedded.
Выберите один из двух подходов.
В Room существует два способа определения и запроса связей между сущностями. Вы можете использовать любой из них:
- Промежуточный класс данных со встроенными объектами, или
- Метод реляционного запроса с возвращаемым типом multimap.
Если у вас нет конкретной причины использовать промежуточные классы данных, мы рекомендуем использовать подход с возвращаемым типом multimap. Подробнее об этом подходе см. в разделе «Возвращение multimap» .
Использование промежуточных классов данных позволяет избежать написания сложных SQL-запросов, но также может привести к увеличению сложности кода, поскольку требует дополнительных классов данных. Короче говоря, подход с возвращаемым типом multimap требует от ваших SQL-запросов большей работы, а подход с промежуточными классами данных требует от вашего кода большей работы.
Используйте подход с промежуточными классами данных.
При использовании промежуточного класса данных вы определяете класс данных, который моделирует взаимосвязь между вашими сущностями Room. Этот класс данных содержит пары между экземплярами одной сущности и экземплярами другой сущности в виде встроенных объектов . Ваши методы запросов затем могут возвращать экземпляры этого класса данных для использования в вашем приложении.
Например, вы можете определить класс данных 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?)
Java
@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 вам не нужно определять какие-либо дополнительные классы данных. Вместо этого вы определяете возвращаемый тип multimap для своего метода на основе желаемой структуры карты и определяете взаимосвязь между вашими сущностями непосредственно в вашем SQL-запросе.
Например, следующий метод запроса возвращает сопоставление экземпляров User и Book , представляющих пользователей библиотеки с конкретными взятыми напрокат книгами:
Котлин
@Query(
"SELECT * FROM user" +
"JOIN book ON user.id = book.user_id"
)
fun loadUserAndBookNames(): Map<User, List<Book>>
Java
@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?
)
Java
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)