使用 Room 实体定义数据

当您使用 Room 持久性库 存储应用数据时,您定义实体来表示要存储的对象。每个实体对应于关联的 Room 数据库中的一个表,并且每个实体实例表示对应表中的一行数据。

这意味着您可以使用 Room 实体来定义您的 数据库模式,而无需编写任何 SQL 代码。

实体的结构

您将每个 Room 实体定义为一个用 @Entity 注解的类。Room 实体包含数据库中对应表中每一列的字段,包括构成 主键 的一列或多列。

以下代码是一个简单实体的示例,它定义了一个 User 表,其中包含 ID、名字和姓氏的列

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

默认情况下,Room 使用类名作为数据库表名。如果希望表具有不同的名称,请设置 tableName 属性 @Entity 注解。类似地,Room 默认使用字段名作为数据库中的列名。如果希望列具有不同的名称,请将 @ColumnInfo 注解添加到字段并设置 name 属性。以下示例演示了表及其列的自定义名称

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

定义主键

每个 Room 实体都必须定义一个 主键,该主键唯一标识数据库对应表中的每一行。最简单的方法是使用 @PrimaryKey 注解单个列。

Kotlin

@PrimaryKey val id: Int

Java

@PrimaryKey
public int id;

定义复合主键

如果需要实体的实例由多列的组合唯一标识,则可以通过在 @EntityprimaryKeys 属性中列出这些列来定义复合主键

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

忽略字段

默认情况下,Room 为实体中定义的每个字段创建一个列。如果实体具有不需要持久化的字段,则可以使用 @Ignore 对其进行注解,如下面的代码片段所示

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

在实体从父实体继承字段的情况下,通常使用 @Entity 属性的 ignoredColumns 属性更容易。

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 支持多种类型的注解,使您能够更轻松地搜索数据库表中的详细信息。除非应用的 minSdkVersion 小于 16,否则请使用全文搜索。

支持全文搜索

如果您的应用需要通过全文搜索 (FTS) 非常快速地访问数据库信息,请让您的实体由使用 FTS3 或 FTS4 SQLite 扩展模块 的虚拟表支持。要使用此功能(在 Room 2.1.0 及更高版本中可用),请将 @Fts3@Fts4 注解添加到给定的实体,如下面的代码片段所示

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

在表支持多种语言内容的情况下,请使用 languageId 选项指定存储每行语言信息的列

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 提供了其他一些定义支持 FTS 的实体的选项,包括结果排序、标记器类型以及作为外部内容管理的表。有关这些选项的更多详细信息,请参阅 FtsOptions 参考。

索引特定列

如果您的应用必须支持不支持 FTS3 或 FTS4 表支持实体的 SDK 版本,您仍然可以在数据库中为某些列添加索引以加快查询速度。要为实体添加索引,请在 indices 属性中包含 @Entity 注解,并列出您想要包含在索引或复合索引中的列的名称。以下代码片段演示了此注释过程。

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

有时,数据库中的某些字段或字段组必须是唯一的。您可以通过将 unique 属性设置为 true 来强制执行此唯一性属性。以下代码示例可防止表中出现两行包含 firstNamelastName 列相同值的相同值集。

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

包含基于 AutoValue 的对象

在 Room 2.1.0 及更高版本中,您可以使用基于 Java 的 不可变值类(使用 @AutoValue 进行注释)作为应用数据库中的实体。当两个实体实例的列包含相同的值时,此支持在将它们视为相等时特别有用。

当使用用 @AutoValue 注释的类作为实体时,您可以使用 @PrimaryKey@ColumnInfo@Embedded@Relation 注释类的抽象方法。但是,在使用这些注释时,您必须每次都包含 @CopyAnnotations 注释,以便 Room 可以正确解释方法的自动生成的实现。

以下代码片段显示了一个用 @AutoValue 注释的类的示例,Room 将其识别为实体。

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