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 está relacionada con otra entidad única.
- **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 estar relacionadas 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 entidad como un campo, y esta
entidad anidada puede contener otras entidades. Esto usa la anotación
@Embedded.
Elige entre dos enfoques
En Room, hay dos formas de definir y realizar búsquedas de una relación entre entidades. Puedes usar lo siguiente:
- Una clase de datos intermedia con objetos incorporados
- Un método de búsqueda relacional con un tipo de datos que se devuelven 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 intermedio
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 de 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 buscar los campos integrados tal como lo harías con otras columnas individuales.
Por ejemplo, tu clase User puede incluir un campo del 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. En el siguiente fragmento de código, se muestra esto:
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 mantener cada
columna única configurando la prefix propiedad. 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)