Cómo definir datos con entidades Room

Cuando usas la biblioteca de persistencias de Room para almacenar los datos de tu app, defines entidades para representar los objetos que deseas almacenar. Cada entidad corresponde a una tabla en la base de datos de Room asociada y cada instancia de una entidad representa una fila de datos en la tabla correspondiente.

Eso significa que puedes usar las entidades de Room para definir tu esquema de base de datos sin escribir ningún código de SQL.

Anatomía de una entidad

Define cada entidad de Room como una clase con @Entity como anotación. Una entidad de Room incluye campos para cada columna de la tabla correspondiente en la base de datos, incluidas una o más columnas que conforman la clave primaria.

El siguiente código es un ejemplo de una entidad simple que define una tabla User con columnas para el ID, el nombre y el apellido:

Kotlin

@Entity
data class User(
    @PrimaryKey val id: Int,

    val firstName: String?,
    val lastName: String?
)

Java

@Entity
public class User {
    @PrimaryKey
    public int id;

    public String firstName;
    public String lastName;
}

De forma predeterminada, Room usa el nombre de la clase como el nombre de la tabla de la base de datos. Si quieres que la tabla tenga un nombre diferente, configura la propiedad tableName de la anotación @Entity. De manera similar, Room usa los nombres de campos como nombres de columna en la base de datos de forma predeterminada. Si quieres que una columna tenga un nombre diferente, agrega la anotación @ColumnInfo al campo y establece la propiedad name. En el siguiente ejemplo, se muestran los nombres personalizados para tablas y columnas:

Kotlin

@Entity(tableName = "users")
data class User (
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

Java

@Entity(tableName = "users")
public class User {
    @PrimaryKey
    public int id;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;
}

Cómo definir una clave primaria

Cada entidad de Room debe definir una clave primaria que identifique de manera única cada fila en la tabla de base de datos correspondiente. La manera más sencilla de hacerlo es anotar una sola columna con @PrimaryKey:

Kotlin

@PrimaryKey val id: Int

Java

@PrimaryKey
public int id;

Cómo definir una clave primaria compuesta

Si necesitas que las instancias de una entidad se identifiquen de forma única mediante una combinación de varias columnas, puedes definir una clave primaria compuesta si enumeras esas columnas en la propiedad primaryKeys de @Entity:

Kotlin

@Entity(primaryKeys = ["firstName", "lastName"])
data class User(
    val firstName: String?,
    val lastName: String?
)

Java

@Entity(primaryKeys = {"firstName", "lastName"})
public class User {
    public String firstName;
    public String lastName;
}

Cómo ignorar campos

De forma predeterminada, Room crea una columna para cada campo que se define en la entidad. Si una entidad tiene campos no deseados, puedes usar la anotación @Ignore en ellos, como se muestra en el siguiente fragmento de código:

Kotlin

@Entity
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    val lastName: String?,
    @Ignore val picture: Bitmap?
)

Java

@Entity
public class User {
    @PrimaryKey
    public int id;

    public String firstName;
    public String lastName;

    @Ignore
    Bitmap picture;
}

Cuando una entidad hereda campos de una entidad principal, suele ser más fácil usar la propiedad ignoredColumns del atributo @Entity:

Kotlin

open class User {
    var picture: Bitmap? = null
}

@Entity(ignoredColumns = ["picture"])
data class RemoteUser(
    @PrimaryKey val id: Int,
    val hasVpn: Boolean
) : User()

Java

@Entity(ignoredColumns = "picture")
public class RemoteUser extends User {
    @PrimaryKey
    public int id;

    public boolean hasVpn;
}

Room admite varios tipos de anotaciones que facilitan la búsqueda de detalles en las tablas de la base de datos. Usa la búsqueda en el texto completo, a menos que la minSdkVersion de tu app sea inferior a 16.

Cómo admitir la búsqueda en el texto completo

Si la app requiere un acceso muy rápido a la información de la base de datos mediante la búsqueda en el texto completo (FTS), respalda tus entidades con una tabla virtual que use el módulo de extensión SQLite FTS3 o FTS4. Para usar esta función, disponible en Room 2.1.0 y versiones posteriores, agrega la anotación @Fts3 o @Fts4 a una entidad dada, como se muestra en el siguiente fragmento de código:

Kotlin

// Use `@Fts3` only if your app has strict disk space requirements or if you
// require compatibility with an older SQLite version.
@Fts4
@Entity(tableName = "users")
data class User(
    /* Specifying a primary key for an FTS-table-backed entity is optional, but
       if you include one, it must use this type and column name. */
    @PrimaryKey @ColumnInfo(name = "rowid") val id: Int,
    @ColumnInfo(name = "first_name") val firstName: String?
)

Java

// Use `@Fts3` only if your app has strict disk space requirements or if you
// require compatibility with an older SQLite version.
@Fts4
@Entity(tableName = "users")
public class User {
    // Specifying a primary key for an FTS-table-backed entity is optional, but
    // if you include one, it must use this type and column name.
    @PrimaryKey
    @ColumnInfo(name = "rowid")
    public int id;

    @ColumnInfo(name = "first_name")
    public String firstName;
}

Cuando una tabla admita contenido en varios idiomas, usa la opción languageId para especificar la columna que almacena la información de idioma de cada fila:

Kotlin

@Fts4(languageId = "lid")
@Entity(tableName = "users")
data class User(
    // ...
    @ColumnInfo(name = "lid") val languageId: Int
)

Java

@Fts4(languageId = "lid")
@Entity(tableName = "users")
public class User {
    // ...

    @ColumnInfo(name = "lid")
    int languageId;
}

Room ofrece varias opciones para definir entidades respaldadas por FTS, entre ellas el orden de los resultados, los tipos de tokenizadores y las tablas que se administran como contenido externo. Para obtener más información sobre estas opciones, consulta la referencia de FtsOptions.

Columnas específicas del índice

Si la app debe admitir versiones de SDK que no permiten el uso de entidades respaldadas por tablas FTS3 o FTS4, igual puedes indexar algunas columnas de la base de datos para agilizar las búsquedas. Para agregar índices a una entidad, incluye la propiedad indices dentro de la anotación @Entity y enumera los nombres de las columnas que quieras incluir en el índice o en el índice compuesto. En el siguiente fragmento de código, se muestra este proceso de anotación:

Kotlin

@Entity(indices = [Index(value = ["last_name", "address"])])
data class User(
    @PrimaryKey val id: Int,
    val firstName: String?,
    val address: String?,
    @ColumnInfo(name = "last_name") val lastName: String?,
    @Ignore val picture: Bitmap?
)

Java

@Entity(indices = {@Index("name"),
        @Index(value = {"last_name", "address"})})
public class User {
    @PrimaryKey
    public int id;

    public String firstName;
    public String address;

    @ColumnInfo(name = "last_name")
    public String lastName;

    @Ignore
    Bitmap picture;
}

A veces, ciertos campos o grupos de campos de una base de datos deben ser únicos. Puedes aplicar esta propiedad de exclusividad con la propiedad unique de una anotación @Index como true. En la siguiente muestra de código, se evita que una tabla tenga dos filas con el mismo conjunto de valores para las columnas firstName y lastName:

Kotlin

@Entity(indices = [Index(value = ["first_name", "last_name"],
        unique = true)])
data class User(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?,
    @Ignore var picture: Bitmap?
)

Java

@Entity(indices = {@Index(value = {"first_name", "last_name"},
        unique = true)})
public class User {
    @PrimaryKey
    public int id;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;

    @Ignore
    Bitmap picture;
}

Cómo incluir objetos basados en AutoValue

En Room 2.1.0 y versiones posteriores, puedes usar clases de valores inmutables basadas en Java, que puedes anotar con @AutoValue, como entidades en la base de datos de la app. La compatibilidad es particularmente útil cuando dos instancias de una entidad se consideran iguales si sus columnas tienen valores idénticos.

Cuando usas clases anotadas con @AutoValue como entidades, puedes anotar los métodos abstractos de la clase con @PrimaryKey, @ColumnInfo, @Embedded y @Relation. Sin embargo, si las usas, debes incluir la anotación @CopyAnnotations cada vez, para que Room pueda interpretar correctamente las implementaciones autogeneradas de los métodos.

En el siguiente fragmento de código, se muestra un ejemplo de una clase anotada con @AutoValue que Room reconoce como una entidad:

User.java

@AutoValue
@Entity
public abstract class User {
    // Supported annotations must include `@CopyAnnotations`.
    @CopyAnnotations
    @PrimaryKey
    public abstract long getId();

    public abstract String getFirstName();
    public abstract String getLastName();

    // Room uses this factory method to create User objects.
    public static User create(long id, String firstName, String lastName) {
        return new AutoValue_User(id, firstName, lastName);
    }
}