Como SQLite es una base de datos relacional, puedes definir relaciones entre entidades. Aunque la mayoría de las bibliotecas de asignación relacional de objetos permiten que los objetos de entidad se hagan referencia entre sí, Room lo prohíbe explícitamente. Para obtener más información sobre el razonamiento técnico que respalda esta decisión, consulta Por qué Room no permite referencias a objetos.
Tipos de relaciones
Room admite los siguientes tipos de relaciones:
- Uno a uno: Representa una relación en la que una sola entidad se relaciona con otra sola entidad.
- Uno a varios: Representa una relación en la que una sola entidad puede estar relacionada con varias entidades de otro tipo.
- Varios a varios: Representa una relación en la que varias entidades de un tipo pueden relacionarse con varias entidades de otro tipo. Por lo general, esto requiere una tabla de unión.
- Relaciones anidadas (con objetos incorporados): Representa una relación en la que una entidad contiene otra como un campo, y esta entidad anidada puede contener otras entidades. Esta opción usa la anotación
@Embedded
.
Elige entre dos enfoques
En Room, hay dos formas de definir y consultar una relación entre entidades. Puedes usar cualquiera de las siguientes opciones:
- Una clase de datos intermedia con objetos incorporados
- Un método de consulta relacional con un tipo de datos que se devuelve de multimapa.
Si no tienes un motivo específico para usar clases de datos intermedios, te recomendamos que uses el enfoque que muestra tipos de datos que se devuelven de multimapa. Para obtener más información sobre este enfoque, consulta Cómo devolver un multimapa.
El enfoque de clase de datos intermedio te permite evitar escribir consultas en SQL complejas, pero también puede dar como resultado una mayor complejidad del código porque requiere clases de datos adicionales. En resumen, el enfoque que muestra tipos de datos que se devuelven de multimapa requiere que tus consultas en SQL hagan más trabajo; y el enfoque de clase de datos intermedio requiere que el código realice más trabajo.
Usa el enfoque de clase de datos intermedia
En el enfoque de clase de datos intermedio, puedes definir una clase de datos que modela la relación entre tus entidades de Room. Esta clase de datos contiene las vinculaciones entre instancias de una entidad e instancias de otra entidad como objetos incorporados. Los métodos de búsqueda pueden devolver instancias de esta clase de datos para usar en tu app.
Por ejemplo, puedes definir una clase de datos UserBook
para representar a los usuarios de la biblioteca que retiraron determinados libros y definir un método de búsqueda para recuperar una lista de instancias UserBook
de la base de datos:
Kotlin
@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;
}
Usa el enfoque de tipos de datos que se devuelven de multimapa
En el método que muestra tipos de datos que se devuelven de multimapa, no necesitas definir ninguna clase de datos adicional. En su lugar, defines el tipo de datos que se devuelven de multimapa para tu método según la estructura de mapa que deseas y que define la relación entre tus entidades directamente en tu consulta en SQL.
Por ejemplo, el siguiente método de búsqueda devuelve una asignación de las instancias User
y Book
para representar a los usuarios de la biblioteca que retiraron determinados libros:
Kotlin
@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();
Cómo crear objetos incorporados
Es posible que, a veces, quieras expresar una entidad o un objeto de datos como un solo elemento integral en la lógica de la base de datos, incluso si el objeto contiene varios campos. En estas situaciones, puedes usar la anotación @Embedded
para representar un objeto cuyos subcampos quieras desglosar en una tabla. Luego, puedes consultar los campos integrados tal como lo harías con otras columnas individuales.
Por ejemplo, la clase User
puede incluir un campo de tipo Address
, que representa una composición de campos llamados street
, city
, state
y postCode
. Para almacenar las columnas compuestas por separado en la tabla, incluye un campo Address
. Esto debería aparecer en la clase User
con la anotación @Embedded
. Esto se demuestra en el siguiente fragmento de código:
Kotlin
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;
}
La tabla que representa un objeto User
contiene columnas con los siguientes nombres: id
, firstName
, street
, state
, city
y post_code
.
Si una entidad tiene varios campos incorporados del mismo tipo, puedes establecer cada columna como única mediante la configuración de la propiedad prefix
. Luego, Room agrega el valor proporcionado al comienzo de cada nombre de columna en el objeto incorporado.
Recursos adicionales
Para obtener más información sobre cómo definir relaciones entre entidades en Room, consulta los siguientes recursos adicionales.
Videos
- Novedades de Room (Android Dev Summit 2019)