前言:
1、在学习本篇之前,请确保你读过Android Room DataBase(一),或者知道如何定义Room的三大组件以及如何简单的使用它们。
2、本篇,将着重讲解Room的Entity(表)。包含如何定义Entity的主键、索引、唯一性 and so on。
3、Sql的基本语法:http://www.w3school.com.cn/sql/index.asp
一、在Room数据库中创建一张表(Entity)。
在Room中,每一个表以一个Java类的形式来表现。但是,必须在该类的类定义的前一行,加上@Entity注解。用以表示该类是Room的一个Entity。下面代码,向你展示,一个Entity是如何被定义:
@Entity
class User {
@PrimaryKey
public int id;
public String firstName;
public String lastName;
@Ignore
Bitmap picture;
}
上面的代码,主要有三个注解,需要进行解释,它们分别是:
1、@Entity,该注解,写在类的定义的前一行,用于表示该类是Room的一个Entity。
2、@PrimaryKey,该注解,写在字段的定义的前一行,用于表示该字段是该Entity的主键。
3、@Ignore,该注解,写在字段的定义的前一行,用于忽略掉那些不需要保存到表中的字段。
这里需要注意的是,Entity类的字段的访问权限,可以是私有的。但是,为了确保其对应的Dao能够访问该字段。需要给私有字段,添加Setter和Getter。而且,它们的命名,必须遵循JavaBean的格式。Android Studio有自动创建Setter和Getter的功能,不懂请百度一下。
另外,Entity类允许,存在不带参的默认构造方法或是带部分或全部参数的构造方法。这里的部分参数或全部参数的参数,指的是,该Entity类里的字段。注意,这些参数必须与Entity里的字段相同。
二、定义及使用表中的主键(PrimaryKey)。
正如“一”中所讲,只要在字段的定义的前一行,添加@PrimaryKey注解即可定义一个表中的主键。
但是,这里有几个点,需要注意:
1、Entity必须包含至少一个主键。每一个Entity,都必须有至少一个主键,哪怕这个Entity里面什么都没有,都得添加一个主键。
2、主键ID的自增(autoGenerate)。只要设置PrimaryKey的autoGenerate属性为true即可。
@PrimaryKey(autoGenerate = true)
private int id;
3、联合主键(primaryKeys)。与单主键不同的是,联合主键的定义,是作为@Entity的属性的形式,被定义的。并且,关键字是primaryKeys,不是primaryKey,多了一个s。代码如下:
@Entity(primaryKeys = {"firstName", "lastName"})
class User {
public String firstName;
public String lastName;
@Ignore
Bitmap picture;
}
4、自定义表名(tableName)。
你可以通过@Entity的tableName属性,来定义一个与类名不同的表名。代码如下:
@Entity(tableName = "users")
public class User {
...
}
5、自定义字段名(ColumnInfo)。
你可以通过@ColumnInfo注解的name属性,来定义一个与字段名不同的列名。代码如下:
@Entity
public class User {
@ColumnInfo(name = "first_name")
private String firstName;
@ColumnInfo(name = "last_name")
private String lastName;
}
三、创建索引与唯一性约束。
我们可以通过创建索引,来提高查询的效率,其原理是索引原理。同时,可以创建字段的唯一性约束,来避免创建相同的数据。因为在一张表中,除了主键(id)字段的每条数据都是不同之外,有可能还存在其它字段的数据也不允许重复,比如身份证号码,这时就需要添加唯一性约束。
1、索引(indices)
创建索引的根本目的,是提高查询的效率。那么,如何在Entity中,创建索引呢?
@Entity(indices = {@Index("name"),
@Index(value = {"last_name", "address"})})
class User {
@PrimaryKey
public int id;
public String firstName;
public String address;
@ColumnInfo(name = "last_name")
public String lastName;
@Ignore
Bitmap picture;
}
2、唯一性约束(unique)
有时候,我们可能想要某一字段,或多个字段的组合的数据,在表中是唯一的,不重复的。那么,我们可以使用唯一性约束。代码如下:
@Entity(indices = {@Index(value = {"first_name", "last_name"},
unique = true)})
class User {
@PrimaryKey
public int id;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
@Ignore
Bitmap picture;
}
四、创建外键(foreignKeys)。
需要注意的是: RoomDataBase不支持Entity里面包含另一个Entity。即便在服务端,这种做法不仅正确,而且可以很好的运行。因为在服务端,这种包含关系,是采用懒(延后)加载机制的,即在需要的时候,再查询。但在客户端,就不是这么一回事了。众所周知,安卓计算和绘制UI的时间是有限的,一般大概超过16ms的就会出现ANR。所以,一些费时的操作,是不允许在UI线程里进行操作的。所以,假如Room支持这一种形式的话,在延时加载另一个Entity的时候,就有可能出现ANR或者明显的卡顿。所以,Room明令禁止Entity中包含,某一字段是另一种Entity类型。
但是,Room 允许通过创建外键的形式,来绑定多个Entity之间的关系,来确保数据的完整性。让我们来看看,如何创建外键:
@Entity(foreignKeys = @ForeignKey(entity = User.class,
parentColumns = "id",
childColumns = "user_id"))
class Book {
@PrimaryKey
public int bookId;
public String title;
@ColumnInfo(name = "user_id")
public int userId;
}
五、在Entity中创建自定义类型字段(Embedded)。
注意:在Entity中创建自定义类型字段,并不意味着,你可以在Entity中创建另一种Entity类型字段。Room,明令禁止多个Entity之间的相互嵌套!
有时候,我们可能想要在Entity中创建一个自定义字段,比如,在User类中,再加一个Address类(非Entity)的字段。那么,该怎么办呢?很简单,Room提供了一个注解@Embedded。只需要在自定义类型的字段的上一行,添加该注解即可。废话不多说,Show you code:
class Address {
public String street;
public String state;
public String city;
@ColumnInfo(name = "post_code")
public int postCode;
}
@Entity
class User {
@PrimaryKey
public int id;
public String firstName;
@Embedded
public Address address;
}
有几点需要注意的:
1、此时,数据表User,并没有一列叫做address的。而是一共有6列数据,分别是:id, firstName, street, state, city, post_code。
2、自定义类型字段里面,可以嵌套另一个自定义类型字段。
基于上面的情况,可能会出现一种情况,就是当一个Entity中存在多个相同的自定义类型的时候,你可能想要为区分每一个自定义类型的字段。这样说,可能不好理解,举个例子,考虑一下这种情况:
1、有一个普通Java类Coordinate,代表坐标,包含两个表示经纬度的字段:longitude 和 latitude。
2、有一个Entity类Coordinates,代表多个国家的坐标,包含两个类型为Coordinate的字段,分别表示中国和英国的坐标:chinaCoordinate和englandCoordinate。
如代码所示:
/**
* @Author 李岳锋
* @CreateTime 2018/2/23
* @Description 讨论@Embedded的prefix属性的作用
**/
@Entity
public class Coordinates {
// 代表中国的坐标,存到表中后,该类的字段会变成latitude和longitude
@Embedded
private Coordinate chinaCoordinate;
// 代表英国的坐标,存到表中后,该类的字段会变成latitude和longitude
@Embedded
private Coordinate englandCoordinate;
}
// 代表坐标
class Coordinate{
@ColumnInfo(name = "latitude")
private long latitude;
@ColumnInfo(name = "longitude")
private long longitude;
}
基于上面的知识,我们可以知道。Entity表只会存在两个字段,分别是latitude和longitude。这样,我们就无法,分别的为中国和英国的坐标做出区分。那当我们想区分二者时,该怎么做呢?同样地,Room为 @Embedded注解提供了一个prefix属性。通过设置该属性,就可以进行区分,确保表中,存在不同的字段。废话不多说,看代码:
/**
* @Author 李岳锋
* @CreateTime 2018/2/23
* @Description 讨论@Embedded的prefix属性的作用
**/
@Entity
public class Coordinates {
// 代表中国的坐标,存到存储库里面后,该类的字段会变成CN_latitude和CN_longitude
@Embedded(prefix = "CN_")
private Coordinate chinaCoordinate;
// 代表英国的坐标,存到存储库里面后,该类的字段会变成UK_latitude和UK_longitude
@Embedded(prefix = "UK_")
private Coordinate englandCoordinate;
}
// 代表坐标
class Coordinate{
@ColumnInfo(name = "latitude")
private long latitude;
@ColumnInfo(name = "longitude")
private long longitude;
}
修改后,Coordinates 表中将会存在四个字段,分别是:CN_latitude、CN_longitude、UK_latitude和UK_longitude。
到此为止,我们已经讲解完Entity的相关知识。下一篇,我们会讲解Dao的相关知识。坚持,就是胜利,不是吗?好了,后续再见! Thank you for reading my posted, Happy New Chinese Year!
Dao:http://blog.csdn.net/l_o_s/article/details/79388408
RoomDataBase基础入门:http://blog.csdn.net/l_o_s/article/details/79346426
官方文档(传送门):https://developer.android.com/training/data-storage/room/defining-data.html