Como definir dados usando entidades do Room

Ao usar a biblioteca de persistência do Room para armazenar os dados do app, você define entidades a fim de representar os objetos que quer armazenar. Cada entidade corresponde a uma tabela no banco de dados associado do Room, e cada instância de uma entidade representa uma linha de dados na tabela correspondente.

Isso significa que é possível usar entidades do Room para definir o esquema do banco de dados (link em inglês) sem que seja necessário programar nenhum código SQL.

Anatomia de uma entidade

Cada entidade do Room é definida como uma classe com a anotação @Entity. Uma entidade do Room inclui campos para cada coluna na tabela correspondente no banco de dados, incluindo uma ou mais colunas que compõem a chave primária.

O código abaixo é o exemplo de uma entidade simples que define uma tabela User com colunas de documento de identificação, nome e sobrenome:

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;
}

Por padrão, o Room usa o nome da classe como nome da tabela do banco de dados. Se você quiser que a tabela tenha um nome diferente, defina a propriedade tableName da anotação @Entity. Da mesma forma, por padrão, o Room usa os nomes dos campos como nomes de colunas no banco de dados. Caso queira que uma coluna tenha um nome diferente, adicione a anotação @ColumnInfo ao campo e defina a propriedade name. Apresentamos nomes personalizados para tabelas e colunas no exemplo abaixo:

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;
}

Definir uma chave primária

Cada entidade do Room precisa definir uma chave primária (link em inglês) que identifique cada linha de maneira exclusiva na tabela do banco de dados correspondente. A maneira mais simples de fazer isso é adicionando a anotação @PrimaryKey a uma única coluna:

Kotlin

@PrimaryKey val id: Int

Java

@PrimaryKey
public int id;

Definir uma chave primária composta

Caso precise que as instâncias de uma entidade sejam identificadas de maneira exclusiva por uma combinação de várias colunas, defina uma chave primária composta listando essas colunas na propriedade 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;
}

Ignorar campos

Por padrão, o Room cria uma coluna para cada campo definido na entidade. Se uma entidade tem campos que você não quer persistir, é possível adicionar a anotação @Ignore a esses campos, conforme mostrado no snippet de código abaixo:

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;
}

Em casos em que uma entidade herda campos de uma entidade pai, geralmente é mais fácil usar a propriedade ignoredColumns do 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;
}

O Room oferece suporte a vários tipos de anotações que facilitam a pesquisa de detalhes nas tabelas do banco de dados. A menos que a minSdkVersion do app seja anterior à 16, use a pesquisa de texto completo.

Suporte a pesquisa de texto completo

Caso o app precise de acesso rápido a informações do banco de dados usando a pesquisa de texto completo (FTS, na sigla em inglês), faça com que as entidades tenham uma tabela virtual que use o módulo de extensão FTS3 ou FTS4 do SQLite (link em inglês). Para usar esse recurso, disponível no Room 2.1.0 e versões mais recentes, adicione a anotação @Fts3 ou @Fts4 a uma entidade, como mostrado no snippet de código abaixo:

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;
}

Em casos em que uma tabela é oferece suporte a conteúdo em vários idiomas, use a opção languageId para especificar a coluna que armazena informações de idioma de cada linha:

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;
}

O Room oferece várias outras opções para definir entidades com apoio do FTS, incluindo ordenação de resultados, tipos de tokenizadores e tabelas gerenciadas como conteúdo externo. Para ver mais detalhes sobre essas opções, consulte a referência FtsOptions.

Indexar colunas específicas

Se o app precisa ser oferecer suporte a versões do SDK que não permitem o uso de entidades com tabelas compatíveis com FTS3- ou FTS4, ainda é possível indexar colunas específicas no banco de dados para acelerar as consultas. Para adicionar índices a uma entidade, inclua a propriedade indices na anotação @Entity, listando os nomes das colunas que você quer incluir no índice simples ou composto. O snippet de código abaixo demonstra esse processo de anotação:

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;
}

Às vezes, alguns campos ou grupos de campos em um banco de dados precisam ser exclusivos. Para aplicar essa exclusividade, defina a propriedade unique de uma anotação @Index como true. O exemplo de código abaixo impede que uma tabela tenha duas linhas que contenham o mesmo conjunto de valores para as colunas firstName e 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;
}

Incluir objetos baseados em AutoValue

No Room 2.1.0 e versões mais recentes, é possível usar classes de valor imutável (link em inglês) baseadas em Java, com a anotação @AutoValue, como entidades no banco de dados do app. Esse suporte é especialmente útil quando duas instâncias de uma entidade são consideradas iguais caso as colunas tenham valores idênticos.

Ao usar classes com a anotação @AutoValue como entidades, você pode adicionar anotações aos métodos abstratos da classe com @PrimaryKey, @ColumnInfo, @Embedded e @Relation. Contudo, ao usar essas anotações, é necessário incluir a anotação @CopyAnnotations todas as vezes, para que o Room possa interpretar corretamente as implementações do método geradas de modo automático.

O snippet de código abaixo mostra um exemplo de uma classe com a anotação @AutoValue que o Room reconhece como uma entidade:

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);
    }
}