使用 Room 实体定义数据

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

使用 Room 实体,您无需编写任何 SQL 代码即可定义数据库架构

实体详解

您可以将每个 Room 实体定义为带有 @Entity 注解的类。Room 实体包含数据库中相应表中的每一列的属性,包括构成主键的一个或多个列。

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

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

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

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

定义主键

您必须为每个 Room 实体定义一个主键,以唯一标识相应数据库表中的每一行。为此,请使用 @PrimaryKey 为单个列添加注解:

@PrimaryKey val id: Int

定义复合主键

如果您需要通过多个列的组合对实体实例进行唯一标识,则可以通过列出 @EntityprimaryKeys 属性中的以下列定义一个复合主键

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

忽略属性

默认情况下,Room 会为实体中定义的每个属性创建一个列。 如需防止 Room 持久保留某个属性,请使用 @Ignore 对其进行注释:

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

如果实体从父实体继承属性,请使用 @Entity 注解的 ignoredColumns 属性:

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

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

Room 支持多种注解,可让您搜索数据库表中的详细信息。

支持全文搜索

如果您的应用需要快速全文搜索 (FTS),请使用虚拟表为您的实体提供支持。使用 FTS3 或 FTS4 SQLite 扩展FTS5 SQLite 扩展

如需使用此功能,请向实体添加 @Fts3@Fts4@Fts5 注解。

// Use `@Fts3` only if your app has strict disk space requirements.
@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 an INTEGER type and column name "rowid".
    @PrimaryKey @ColumnInfo(name = "rowid") val id: Long,
    @ColumnInfo(name = "first_name") val firstName: String
)

如需自定义 FTS 表中数据库信息的令牌化方式,请使用 tokenizer 选项。Room 通过 FtsOptions 提供多个内置分词器,包括 TOKENIZER_SIMPLETOKENIZER_PORTERTOKENIZER_UNICODE61

@Fts4(tokenizer = FtsOptions.TOKENIZER_UNICODE61)
@Entity(tableName = "users")
data class User(
    @PrimaryKey @ColumnInfo(name = "rowid") val id: Long,
    @ColumnInfo(name = "first_name") val firstName: String
)

Room 提供了用于定义由 FTS 支持的实体的其他几个选项,包括结果排序、从列中移除索引以及作为外部内容管理的表。如需详细了解这些选项,请参阅 FtsOptions 参考文档。

将特定列编入索引

如果您使用的是 AndroidSQLiteDriver,并且需要支持不允许使用由 FTS3、FTS4 或 FTS5 表支持的实体的 SDK 版本,您仍可以将数据库中的某些列编入索引,以加快查询速度。如果您使用 BundledSQLiteDriver,无论 Android SDK 版本如何,Room 都支持所有 FTS 版本。

如需为实体添加索引,请在 @Entity 注释中添加 indices 属性。列出要包含在索引或复合索引中的列名称。以下代码段展示了如何添加索引:

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

有时,数据库中的某些列或列组需要包含唯一值。如需强制实施此唯一性,请将 @Index 注释的 unique 属性设为 true。以下代码示例展示了如何强制执行此唯一性:

@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,
)