Android Developers官方文档翻译:Room(二)

使用Room实体类来定义数据

当使用Room持久化类库时,你可以通过实体类(entities)来定义字段和他们之间的关系。关联的Database对象将会为每个实体创建一张数据表。

提醒:开始使用ROOM前,请先添加Adding Components to your Project相关依赖到你app module目录的build.gradle文件中。

默认情况下,Room会为实体类中的每一个属性创建一个数据表的字段。如果一个实体类中包含你不想持久化保存在数据表中的属性,你可以使用@Ignore注解来表示该属性。

下面例子将展示如何使用@Ignore注解:

1
2
3
4
5
6
7
8
9
10
11
@Entity
public class User {
@PrimaryKey
public int id;

public String firstName;
public String lastName;

@Ignore
Bitmap picture;
}

要持久化一个属性,Room必须有访问它的权限。你可以使用public修饰符修饰这些属性,或者提供相对应的getter和setter方法。如果你是使用getter和setter方法的话,记住他们是给予JavaBeans来转化为Room能访问的实例的。

提醒:实体类可以不实现构造方法(即提供默认的无参构造方法,前提是相关联的DAO对象可以访问实体类的属性),或者提供包含属性的构造方法,使得在构造的时候类型和属性名称能一一对应的。Room可以使用完整参数对应构造方法,同时也能使用部分参数对应构造方法,比如一个构造方法只接收部分属性的值的。

使用主键(primary key)

每一个实体类必须定义至少一个主键,就算你的实体类只有一个属性你也依然需要用到@PrimaryKey注解来定义主键。另外,如果你希望Room中主键是自增长的ID字段,你可以在@primaryKey注解中设置autoGenerate)属性。如果你的实体有多个属性联合起来组成主键,你可以在@Entity注解中使用primaryKeys属性来指定他们,示例如下:

1
2
3
4
5
6
7
8
@Entity(primaryKeys = {"firstName", "lastName"})
public class User {
public String firstName;
public String lastName;

@Ignore
Bitmap picture;
}

默认情况下,Room使用类名作为数据库中数据表的名称。如果你需要特别指定一个数据表的名称,可以在@Entity注解中使用tableName特性(property其实翻译属性应该好理解,但这里因为使用属性来表示实体类的属性,避免混淆)指定,示例如下:

1
2
3
4
@Entity(tableName = "users")
public class User {
...
}

注意:在SQLite中,表名是大小写敏感的。

和tableName特性类似,Room使用实体类的属性名作为数据表中的字段名。如果需要定义跟属性名不一样的字段名,需要使用注解@ColumnInfo来指定,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity(tableName = "users")
public class User {
@PrimaryKey
public int id;

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

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

@Ignore
Bitmap picture;
}

索引和唯一性的注解

取决于你想怎样去获取数据,如果你希望加快数据库的查询速度,就需要在实体类中增加一个索引,增加索引的方式是在@Entity注解中使用indices特性,列出你想要的单个索引字段或者组合索引字段,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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字段的时候,要保证他们组合是唯一的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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;
}

在对象间建立关联

因为SQLite是一种关系型数据库,因此你可以在不同对象中间建立起关联关系。需要注意的是,即使大多数对象关系映射的库支持对象之间的相互引用,但Room是明令禁止这么做的。具体的原因可以查看:Understand why Room doesn’t allow object references

虽然不能建立直接的关联,但Room还是允许使用外键(Foreign Key)来约束实体间的关系的。

举个例子,如果一个用户(User)跟一本书(Book)相关联的时候(用户表中的id字段对应书本表中的user_id字段),这个时候就可以使用@ForeignKey注解来定义他们之间的关联关系,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
@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将会删除该User实例的user_id对应的所有书籍。

提醒:SQLite将@Insert(onConflict = REPLACE)作为一组REMOVE和REPLACE操作处理,而不是直接UPDATE。这种替换冲突值的方法可能会影响你的外键约束。更多信息可以查阅:SQLite documentation中的ON CONFLICT clause一节。

创建嵌套对象

有时候,你希望将一些实体或者plain old Java object(POJO,简单的Java对象,实际就是普通JavaBeans)作为数据库逻辑中的一个整体,即使这个实体对象包含有多个属性。在这种情况下,可以使用@Embedded注解来表示要分解到表中子字段的对象。然后,你就可以像查询其他单个字段一样查询所嵌入的字段。

举个例子:User实体中包含一个Address的结构,Address结构中又包含有street,city,state和postCode属性。要将这种嵌套结构保存在数据表中,需要在User实体中使用@Embedded注解来标识address的属性,示例代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
}

上面例子代码所创建的数据表最终将包含以下字段:id,firstName,street,state,city和post_code。

提醒:通过@Embedded注解的属性里面依旧可以包含@Embedded注解的子属性,这个是可以多层嵌套的。

如果一个实体中包含相同类型的embedded属性,为了避免字段名冲突,你可以使用prefix)特性来指定相同类型属性的不容子属性前缀。

示例如下:

1
2
@Embedded(prefix = "foo_")
Coordinates coordinates;

原文链接:https://developer.android.com/training/data-storage/room/defining-data

文章作者: Kevin Wu
文章链接: https://kevinwu.cn/p/3c60768a/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 KevinWu.CN
支付宝打赏