版权声明:本文为博主原创文章,欢迎大家转载!
但是转载请标明出处: https://blog.youkuaiyun.com/t000818/article/details/84258618 ,本文出自:【唐宏宇的博客】
使用Room持久性库时,可以将相关字段集定义为实体。对于每个实体,在关联的Database对象中创建一个表来保存项目。必须通过Database类中的entities数组引用实体类。
注意: 要在应用程序中使用实体,请将 Architecture Components artifacts 添加到应用程序的build.gradle文件中。
以下代码段显示了如何定义实体:
Kotlin写法:
@Entity
data class User(
@PrimaryKey var id: Int,
var firstName: String?,
var lastName: String?
)
Java写法:
@Entity
public class User {
@PrimaryKey
public int id;
public String firstName;
public String lastName;
}
如果要持久化一个字段,Room必须能够访问它。您可以将字段设为public,也可以为其提供getter和setter。如果使用getter和setter方法,请记住它们基于Room中的JavaBeans约定。
注意:实体可以有一个空构造函数 (如果相应的 DAO 类可以访问每个持久化字段), 也可以有一个构造函数, 其参数包含与实体中的字段的类型和名称相匹配的类型和名称。Room 还可以使用完整或部分构造函数, 例如只接收某些字段的构造函数
定义主键
每个实体必须至少定义一个字段作为主键。即使只有一个字段,您仍然需要使用@PrimaryKey注释来注释该字段。此外,如果您希望Room为实体分配自动ID,您可以设置@ PrimaryKey的autoGenerate属性。如果实体具有复合主键,则可以使用@Entity批注的primaryKeys属性,如以下代码段所示:
Kotlin写法:
@Entity(primaryKeys = arrayOf("firstName", "lastName"))
data class User(
@PrimaryKey var id,
var firstName: String?,
var lastName: String?
)
Java写法:
@Entity(primaryKeys = {"firstName", "lastName"})
public class User {
public String firstName;
public String lastName;
}
默认情况下,Room使用类名作为数据库表名。如果希望表具有不同的名称,请设置@Entity批注的tableName属性,如以下代码段所示:
Kotlin写法:
@Entity(tableName = "users")
data class User (
// ...
)
Java写法:
@Entity(tableName = "users")
public class User {
// ...
}
警告:SQLite中的表名称不区分大小写。
与tableName属性类似,Room使用字段名称作为数据库中的列名称。如果希望列具有不同的名称,请将@ColumnInfo注释添加到字段中,如以下代码段所示:
Kotlin写法:
@Entity(tableName = "users")
data class User (
@PrimaryKey var id: Int,
@ColumnInfo(name = "first_name") var firstName: String?,
@ColumnInfo(name = "last_name") var 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会为实体中定义的每个字段创建一列。如果实体有想忽略的字段,则可以使用@Ignore对
它们进行注释
忽略,如下面的代码片段所示:
Kotlin写法:
@Entity
data class User(
@PrimaryKey var id: Int,
var firstName: String?,
var lastName: String?,
@Ignore var 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 = arrayOf("picture"))
data class RemoteUser(
@PrimaryKey var id: Int,
var 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 extension module.的虚拟表支持。要使用Room 2.1.0及更高版本中提供的此功能,请将@Fts3
或@Fts4
注释添加到给定实体,如以下代码段所示:
Kotlin写法:
// 仅当你的应用程序具有严格的磁盘空间要求或者您需要与较旧的SQLite版本兼容时,才使用`@ Fts3`。
@Fts4
@Entity(tableName = "users")
data class User(
/* 为FTS表支持的实体指定主键是可选的,但是
如果包含一个,则必须使用此类型和列名称 */
@PrimaryKey @ColumnInfo(name = "rowid") var id: Int,
@ColumnInfo(name = "first_name") var firstName: String?
)
Java写法:
// 仅当你的应用程序具有严格的磁盘空间要求或者您需要与较旧的SQLite版本兼容时,才使用`@ Fts3`
@Fts4
@Entity(tableName = "users")
public class User {
// 为FTS表支持的实体指定主键是可选的,但是如果包含一个,则必须使用此类型和列名称
@ColumnInfo(name = "rowid")
public int id;
@ColumnInfo(name = "first_name")
public String firstName;
}
注意: 启用FTS的表始终使用INTEGER类型的主键和列名“rowid”。如果您的FTS表支持的实体定义主键,则它必须使用该类型和列名称.
如果表支持多种语言的内容,请使用languageId选项指定存储每行语言信息的列:
Kotlin写法:
@Fts4(languageId = "lid")
@Entity(tableName = "users")
data class User(
// ...
@ColumnInfo(name = "lid") var languageId: Int
)
Java写法:
@Fts4(languageId = "lid")
@Entity(tableName = "users")
public class User {
// ...
@ColumnInfo(name = "lid")
int languageId;
}
Room提供了其他几个用于定义FTS支持的实体的选项,包括结果排序,tokenizer类型和作为外部内容管理的表。有关这些选项的更多详细信息,请参阅FtsOptions
参考。
索引表中特定的列
如果你的应用必须支持的SDK版本不允许使用FTS3或FTS4表支持的实体,仍然可以索引数据库中的某些列以加快查询速度。要向实体添加索引,请在@Entity注解中添加indices属性,列出要包含的索引或复合索引中的列的名称。以下代码片段演示了此注截过程:
Kotlin写法:
@Entity(indices = arrayOf(Index(value = ["last_name", "address"])))
data class User(
@PrimaryKey var id: Int,
var firstName: String?,
var address: String?,
@ColumnInfo(name = "last_name") var lastName: String?,
@Ignore var 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;
}
有时,数据库中的某些字段或字段组必须是唯一的。您可以通过在@Index
注解中设置unique
属性索引注解为true,来强制使这些属性保持唯一性。以下代码示例可防止表具有两行,这些行包含firstName和lastName列的相同值集:
Kotlin写法:
@Entity(indices = arrayOf(Index(value = ["first_name", "last_name"],
unique = true)))
data class User(
@PrimaryKey var id: Int,
@ColumnInfo(name = "first_name") var firstName: String?,
@ColumnInfo(name = "last_name") var 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的对象
注意:此功能仅适用于基于Java的实体。要在基于Kotlin的实体中实现相同的功能,最好使用data classes数据类。
在2.1.0及更高版本的Room中,您可以使用基于Java的 immutable value classes不可变值类(使用@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);
}
}
定义对象间的关系
由于SQLite是关系数据库,因此您可以指定对象之间的关系。尽管大多数对象关系映射库允许实体对象相互引用,但Room明确禁止这样做。要了解此决策背后的技术推理,请参阅了解Room为何不允许对象引用Understand why Room doesn't allow object references。
即使您不能使用直接关系,Room仍允许您在实体之间定义外键约束。
例如,如果存在另一个名为Book的实体,则可以使用该实体定义其与User实体的关系@ForeignKey注解
,如下面的代码片段所示:
Kotlin写法:
@Entity(foreignKeys = arrayOf(ForeignKey(
entity = User::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("user_id"))
)
)
data class Book(
@PrimaryKey var bookId: Int,
var title: String?,
@ColumnInfo(name = "user_id") var userId: Int
)
Java写法:
@Entity(foreignKeys = @ForeignKey(entity = User.class,
parentColumns = "id",
childColumns = "user_id"))
public class Book {
@PrimaryKey
public int bookId;
public String title;
@ColumnInfo(name = "user_id")
public int userId;
}
外键非常强大,因为它们允许您指定更新引用实体时发生的情况。例如,如果通过在@ForeignKey
注解中包含onDelete = CASCADE
,当相应的User实例被删除是,则可以通知SQLite删除该用户的所有书籍。
注意:SQLite将 @Insert(onConflict = REPLACE)
作为一组REMOVE和REPLACE操作处理,而不是单个UPDATE操作。这种替换冲突值的方法可能会影响您的外键约束。有关更多详细信息,请参阅ON_CONFLICT子句的 SQLite documentation文档。
创建嵌套对象
有时,您希望在数据库逻辑中将实体或普通旧Java对象(POJO)表达为一个整体,即使该对象包含多个字段。在这些情况下,您可以使用@Embedded
注解来表示对象你想“分解成表格中的子字段”。然后,您可以像查找其他单个列一样查询嵌入字段。
例如,我们的User类可以包含Address类型的字段,它表示名为street,city,state和postCode的字段的组合。若要在表中单独存储组合列,请在User类中包含一个带@Embedded
注解的Address字段,如下面的代码片段所示
Kotlin写法:
data class Address(
var street: String?,
var state: String?,
var city: String?,
@ColumnInfo(name = "post_code") var postCode: Int
)
@Entity
data class User(
@PrimaryKey var id: Int,
var firstName: String?,
@Embedded var 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;
}
上面表示User对象的表包含具有以下名称的列:id,firstName,street,state,city和post_code。
注意:嵌入字段还可以包含其他嵌入字段
如果实体具有多个相同类型的嵌入字段,则可以通过设置prefix属性使每个列保持唯一。然后,Room将提供的值添加到嵌入对象中每个列名称的开头。