Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Cómo definir relaciones entre objetos

Como SQLite es una base de datos relacional, puedes especificar 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.

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 esas 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 en la clase User con anotaciones @Embedded, como se muestra 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.

Cómo definir relaciones de uno a uno

En las relaciones de uno a uno entre dos entidades, cada instancia de la entidad principal se corresponde exactamente con una instancia de la entidad secundaria y viceversa.

Por ejemplo, imagina una app de streaming de música en la que el usuario tiene una biblioteca de canciones de su propiedad. Cada usuario tiene una sola biblioteca y cada biblioteca corresponde exactamente a un usuario. Por lo tanto, debe haber una relación de uno a uno entre la entidad User y la entidad Library.

Primero, crea una clase para cada una de las dos entidades. Una de las entidades debe incluir una variable que haga referencia a la clave primaria de la otra entidad.

Kotlin

    @Entity
    data class User(
        @PrimaryKey val userId: Long,
        val name: String,
        val age: Int
    )

    @Entity
    data class Library(
        @PrimaryKey val libraryId: Long,
        val userOwnerId: Long
    )
    

Java

    @Entity
    public class User {
        @PrimaryKey public long userId;
        public String name;
        public int age;
    }

    @Entity
    public class Library {
        @PrimaryKey public long libraryId;
        public long userOwnerId;
    }
    

A fin de consultar la lista de usuarios y las bibliotecas correspondientes, primero debes modelar la relación de uno a uno entre las dos entidades. Para ello, crea una clase de datos nueva en la que cada instancia tenga una instancia de la entidad principal y la instancia correspondiente de la entidad secundaria. Agrega la anotación @Relation a la instancia de la entidad secundaria, y asigna a parentColumn el nombre de la columna de clave primaria de la entidad principal y a entityColumn el nombre de la columna de la entidad secundaria que hace referencia a la clave primaria de la entidad principal.

Kotlin

    data class UserAndLibrary(
        @Embedded val user: User,
        @Relation(
             parentColumn = "userId",
             entityColumn = "userOwnerId"
        )
        val library: Library
    )
    

Java

    public class UserAndLibrary {
        @Embedded public User user;
        @Relation(
             parentColumn = "userId",
             entityColumn = "userOwnerId"
        )
        public Library library;
    }
    

Por último, agrega un método a la clase DAO que muestre todas las instancias de la clase de datos que vincula la entidad principal con la secundaria. Este método requiere que Room ejecute dos consultas, así que agrega la anotación @Transaction al método para asegurarte de que toda la operación se realice automáticamente.

Kotlin

    @Transaction
    @Query("SELECT * FROM User")
    fun getUsersAndLibraries(): List<UserAndLibrary>
    

Java

    @Transaction
    @Query("SELECT * FROM User")
    public List<UserAndLibrary> getUsersAndLibraries();
    

Cómo definir relaciones de uno a varios

En las relaciones de uno a varios entre dos entidades, cada instancia de la entidad principal corresponde a cero o más instancias de la entidad secundaria, pero cada instancia de la entidad secundaria solo puede corresponder una instancia de la entidad principal.

En el ejemplo de la app de streaming de música, supongamos que el usuario puede organizar las canciones en listas de reproducción. Cada usuario puede crear tantas listas de reproducción como desee, pero un solo usuario crea cada lista de reproducción. Por lo tanto, debería haber una relación de uno a varios entre la entidad User y la entidad Playlist.

Primero, crea una clase para cada una de las dos entidades. Como en el ejemplo anterior, la entidad secundaria debe incluir una variable que haga referencia a la clave primaria de la entidad principal.

Kotlin

    @Entity
    data class User(
        @PrimaryKey val userId: Long,
        val name: String,
        val age: Int
    )

    @Entity
    data class Playlist(
        @PrimaryKey val playlistId: Long,
        val userCreatorId: Long,
        val playlistName: String
    )
    

Java

    @Entity
    public class User {
        @PrimaryKey public long userId;
        public String name;
        public int age;
    }

    @Entity
    public class Playlist {
        @PrimaryKey public long playlistId;
        public long userCreatorId;
        public String playlistName;
    }
    

A fin de consultar la lista de usuarios y las listas de reproducción correspondientes, primero debes modelar la relación de uno a varios entre las dos entidades. Para ello, crea una clase de datos nueva en la que cada instancia tenga una instancia de la entidad principal y una lista de todas las instancias de entidades secundarias correspondientes. Agrega la anotación @Relation a la instancia de la entidad secundaria, y asigna a parentColumn el nombre de la columna de clave primaria de la entidad principal y a entityColumn el nombre de la columna de la entidad secundaria que hace referencia a la clave primaria de la entidad principal.

Kotlin

    data class UserWithPlaylists(
        @Embedded val user: User,
        @Relation(
              parentColumn = "userId",
              entityColumn = "userCreatorId"
        )
        val playlists: List<Playlist>
    )
    

Java

    public class UserWithPlaylists {
        @Embedded public User user;
        @Relation(
             parentColumn = "userId",
             entityColumn = "userCreatorId"
        )
        public List<Playlist> playlists;
    }
    

Por último, agrega un método a la clase DAO que muestre todas las instancias de la clase de datos que vincula la entidad principal con la secundaria. Este método requiere que Room ejecute dos consultas, así que agrega la anotación @Transaction al método para asegurarte de que toda la operación se realice automáticamente.

Kotlin

    @Transaction
    @Query("SELECT * FROM User")
    fun getUsersWithPlaylists(): List<UserWithPlaylists>
    

Java

    @Transaction
    @Query("SELECT * FROM User")
    public List<UserWithPlaylists> getUsersWithPlaylists();
    

Cómo definir relaciones de varios a varios

En las relaciones de varios a varios entre dos entidades, cada instancia de la entidad principal corresponde a cero o más instancias de la entidad secundaria y viceversa.

En el ejemplo de la app de streaming de música, imagina nuevamente las listas de reproducción definidas por el usuario. Cada lista de reproducción puede incluir muchas canciones y cada canción puede ser parte de muchas listas diferentes. Por lo tanto, debería haber una relación de varios a varios entre la entidad Playlist y la entidad Song.

Primero, crea una clase para cada una de las dos entidades. Las relaciones de varios a varios son diferentes de otros tipos de relación porque, por lo general, no hay una referencia a la entidad principal en la entidad secundaria. Entonces, crea una tercera clase para representar una entidad asociativa (o tabla de referencias cruzadas) entre las dos entidades. La tabla de referencias cruzadas debe tener columnas para la clave primaria de cada entidad contemplada en la relación de varios a varios que se representa en la tabla. En este ejemplo, cada fila de la tabla de referencias cruzadas corresponde a una vinculación de una instancia Playlist y una instancia Song donde la canción a la que se hace referencia se incluye en la lista de reproducción a la que se hace referencia.

Kotlin

    @Entity
    data class Playlist(
        @PrimaryKey val playlistId: Long,
        val playlistName: String
    )

    @Entity
    data class Song(
        @PrimaryKey val songId: Long,
        val songName: String,
        val artist: String
    )

    @Entity(primaryKeys = ["playlistId", "songId"])
    data class PlaylistSongCrossRef(
        val playlistId: Long,
        val songId: Long
    )
    

Java

    @Entity
    public class Playlist {
        @PrimaryKey public long playlistId;
        public String playlistName;
    }

    @Entity
    public class Song {
        @PrimaryKey public long songId;
        public String songName;
        public String artist;
    }

    @Entity(primaryKeys = {"playlistId", "songId"})
    public class PlaylistSongCrossRef {
        public long playlistId;
        public long songId;
    }
    

El siguiente paso depende de cómo quieras consultar las entidades relacionadas.

  • Si quieres consultar listas de reproducción y un listado de las canciones correspondientes por cada lista de reproducción, crea una clase de datos nueva con un objeto Playlist único y un listado de todos los objetos Song que incluye la lista de reproducción.
  • Si quieres consultar canciones y un listado de las listas de reproducción correspondientes por cada canción, crea una clase de datos nueva con un objeto Song único y un listado de los objetos Playlist en los que se incluye la canción.

En ambos casos, modela la relación entre las entidades mediante la propiedad associateBy en la anotación @Relation de cada una de las clases para identificar la entidad de la referencia cruzada que proporciona la relación entre las entidades Playlist y Song.

Kotlin

    data class PlaylistWithSongs(
        @Embedded val playlist: Playlist,
        @Relation(
             parentColumn = "playlistId",
             entityColumn = "songId",
             associateBy = @Junction(PlaylistSongCrossRef::class)
        )
        val songs: List<Song>
    )

    data class SongWithPlaylists(
        @Embedded val song: Song,
        @Relation(
             parentColumn = "songId",
             entityColumn = "playlistId",
             associateBy = @Junction(PlaylistSongCrossRef::class)
        )
        val playlists: List<Playlist>
    )
    

Java

    public class PlaylistWithSongs {
        @Embedded public Playlist playlist;
        @Relation(
             parentColumn = "playlistId",
             entityColumn = "songId",
             associateBy = @Junction(PlaylistSongCrossref.class)
        )
        public List<Song> songs;
    }

    public class SongWithPlaylists {
        @Embedded public Song song;
        @Relation(
             parentColumn = "songId",
             entityColumn = "playlistId",
             associateBy = @Junction(PlaylistSongCrossref.class)
        )
        public List<Playlist> playlists;
    }
    

Por último, agrega un método a la clase DAO para exponer la funcionalidad de consulta que necesita la app.

  • getPlaylistsWithSongs: Este método consulta la base de datos y muestra todos los objetos PlaylistWithSongs resultantes.
  • getSongsWithPlaylists: Este método consulta la base de datos y muestra todos los objetos SongWithPlaylists resultantes.

Cada uno de los métodos requiere que Room ejecute dos consultas, así que agrega la anotación @Transaction a ambos para asegurarte de que toda la operación se realice automáticamente.

Kotlin

    @Transaction
    @Query("SELECT * FROM Playlist")
    fun getPlaylistsWithSongs(): List<PlaylistWithSongs>

    @Transaction
    @Query("SELECT * FROM Song")
    fun getSongsWithPlaylists(): List<SongWithPlaylists>
    

Java

    @Transaction
    @Query("SELECT * FROM Playlist")
    public List<PlaylistWithSongs> getPlaylistsWithSongs();

    @Transaction
    @Query("SELECT * FROM Song")
    public List<SongWithPlaylists> getSongsWithPlaylists();
    

Cómo definir relaciones anidadas

Es posible que, en ocasiones, debas consultar un conjunto de tres o más tablas relacionadas entre sí. En ese caso, definirías relaciones anidadas entre las tablas.

Supongamos que, en el ejemplo de la app de streaming de música, quieres consultar todos los usuarios, todas las listas de reproducción de cada uno de ellos y todas las canciones de cada lista de cada usuario. Los usuarios tienen una relación uno a varios con las listas de reproducción, y estas tienen una relación varios a varios con las canciones. En el siguiente ejemplo de código, se muestran las clases que representan las entidades, así como la tabla de referencias cruzadas de la relación de varios a varios entre las listas de reproducción y las canciones:

Kotlin

    @Entity
    data class User(
        @PrimaryKey val userId: Long,
        val name: String,
        val age: Int
    )

    @Entity
    data class Playlist(
        @PrimaryKey val playlistId: Long,
        val userCreatorId: Long,
        val playlistName: String
    )

    @Entity
    data class Song(
        @PrimaryKey val songId: Long,
        val songName: String,
        val artist: String
    )

    @Entity(primaryKeys = ["playlistId", "songId"])
    data class PlaylistSongCrossRef(
        val playlistId: Long,
        val songId: Long
    )
    

Java

    @Entity
    public class User {
        @PrimaryKey public long userId;
        public String name;
        public int age;
    }

    @Entity
    public class Playlist {
        @PrimaryKey public long playlistId;
        public long userCreatorId;
        public String playlistName;
    }
    @Entity
    public class Song {
        @PrimaryKey public long songId;
        public String songName;
        public String artist;
    }

    @Entity(primaryKeys = {"playlistId", "songId"})
    public class PlaylistSongCrossRef {
        public long playlistId;
        public long songId;
    }
    

Primero, modela la relación entre dos de las tablas del conjunto como lo harías normalmente, con una clase de datos y la anotación @Relation. En el siguiente ejemplo, se muestra una clase PlaylistWithSongs que modela una relación de varios a varios entre las clases de entidad Playlist y Song:

Kotlin

    data class PlaylistWithSongs(
        @Embedded val playlist: Playlist,
        @Relation(
             parentColumn = "playlistId",
             entityColumn = "songId",
             associateBy = @Junction(PlaylistSongCrossRef::class)
        )
        val songs: List<Song>
    )
    

Java

    public class PlaylistWithSongs {
        @Embedded public Playlist playlist;
        @Relation(
             parentColumn = "playlistId",
             entityColumn = "songId",
             associateBy = @Junction(PlaylistSongCrossRef.class)
        )
        public List<Song> songs;
    }
    

Después de definir una clase de datos que represente esa relación, crea otra clase de datos que modele la relación entre otra tabla del conjunto y la primera clase de relación, y "anida" la relación existente dentro de la nueva. En el siguiente ejemplo, se muestra una clase UserWithPlaylistsAndSongs que modela una relación de uno a varios entre la clase de entidad User y la clase de relación PlaylistWithSongs:

Kotlin

    data class UserWithPlaylistsAndSongs {
        @Embedded val user: User
        @Relation(
            entity = Playlist::class,
            parentColumn = "userId",
            entityColumn = "userCreatorId"
        )
        val playlists: List<PlaylistWithSongs>
    }
    

Java

    public class UserWithPlaylistsAndSongs {
        @Embedded public User user;
        @Relation(
            entity = Playlist.class,
            parentColumn = "userId",
            entityColumn = "userCreatorId"
        )
        public List<PlaylistWithSongs> playlists;
    }
    

La clase UserWithPlaylistsAndSongs modela indirectamente las relaciones entre las tres clases de entidad User, Playlist y Song. Esto se ilustra en la figura 1.

UserWithPlaylistsAndSongs modela la relación entre User y PlaylistWithSongs, que a su vez modela la relación entre Playlist y Song.

Figura 1. Diagrama de clases de relaciones en el ejemplo de la app de streaming de música

Si hay más tablas en el conjunto, crea una clase para modelar la relación entre cada tabla restante y la clase de relación que modela las relaciones entre todas las tablas anteriores. Así se crea una cadena de relaciones anidadas entre todas las tablas que quieres consultar.

Por último, agrega un método a la clase DAO para exponer la funcionalidad de consulta que necesita la app. El método requiere que Room ejecute varias consultas, así que agrega la anotación @Transaction para asegurarte de que toda la operación se realice automáticamente:

Kotlin

    @Transaction
    @Query("SELECT * FROM User")
    fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>
    

Java

    @Transaction
    @Query("SELECT * FROM User")
    public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();
    

Recursos adicionales

Para obtener más información sobre cómo definir relaciones entre entidades en Room, consulta los siguientes recursos adicionales.

Ejemplos

Videos

Blogs