Room系列专题
Android Jetpack之Room篇
Room Entity注解说明
Room Dao注解说明
Room Fts 虚拟表模块
Room DatabaseView 视图
Room SkipQueryVerification
Room TypeConverter 属性类型转换器
先导

我们先看一个例子,通过这个例子来查看每个注解具体的含义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
@Entity(tableName = "users")
public class User {
@NonNull
@PrimaryKey//每个bean类都必须要声明一个主键,除非父类声明了。
@ColumnInfo(name = "userid")
private String mId;
@ColumnInfo(name = "username")
private String mUserName;
@Ignore
public User(String userName) {
mId = UUID.randomUUID().toString();
mUserName = userName;
}
public User(String id, String userName) {
this.mId = id;
this.mUserName = userName;
}
public String getId() {
return mId;
}
public String getUserName() {
return mUserName;
}
}
|
注解说明:
Entity 实体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Entity {
//SQLite数据库中的表名。如果没有设置,则默认为类名
String tableName() default "";
//表上的索引列表
Index[] indices() default {};
//是否继承父类的索引。默认值为false。
boolean inheritSuperIndices() default false;
//复合主键,主键列名的列表。如果您想定义一个自动生成的主键,可以在字段上添加的注解 @PrimaryKey(autoGenerate = true)
String[] primaryKeys() default {};
//外键,可以在属性上添加 @ForeignKey(entity = , parentColumns = , childColumns = )注解
ForeignKey[] foreignKeys() default {};
//忽略的列名列表。可以在属性上添加 @Ignore 注解
String[] ignoredColumns() default {};
}
|
- 这个类在数据库中有一个映射SQLite表。
- 每个实体必须至少有一个用 PrimaryKey注释的字段。
- 每个实体必须要么有一个无参数构造函数,要么有一个参数匹配字段的构造函数(基于类型和名称)。构造函数不必接收所有字段作为参数,但是如果没有将字段传递给构造函数,那么它要么是公共的,要么有一个公共setter。如果有匹配的构造函数可用,Room将始终使用它。如果不希望它使用构造函数,可以使用 Ignore注释它。
- 当一个类被标记为一个实体时,它的所有字段都被持久化。如果您想排除它的一些字段,您可以用 Ignore标记它们。
- 如果字段是 transient,则自动忽略它**,除非**用 ColumnInfo、Embedded或 relationship注释它。
PrimaryKey 主键
字段定义
1
2
3
4
5
6
|
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface PrimaryKey {
//将其设置为true,SQLite会生成惟一的id。
boolean autoGenerate() default false;
}
|
- 单一主键,如果想创建多个主键,那么可以通过**@Entity(primaryKeys= [])**在类上定义。
- 每个bean类都必须要声明一个主键,除非父类声明了。
- 如果这个属性被**@Embedded** 定义,在定义**@PrimaryKey** 那么就会成为复合主键
例子
1
2
3
4
5
6
7
8
9
10
|
public class Coordinates {
double latitude;
double longitude;
}
@Entity
public class Address {
@PrimaryKey
@Embedded
Coordinates coordinates;
}
|
Embedded 嵌套字段
字段定义
1
2
3
4
5
6
|
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface Embedded {
//@Embedded(prefix = "loc_"),将会在列中生成loc_latitude 的列名
String prefix() default "";
}
|
- 如果加上这个字段就会创建多个列,列名就是自定好的属性字段。
- 如果你想查询Coordinates 类的 2 个属性,那么就会返回这个类。
- 如果与子对象和所有者对象的字段存在名称冲突,则可以为子对象的字段指定 prefix。
例子
1
2
3
4
5
6
7
8
9
10
|
public class Coordinates {
double latitude;
double longitude;
}
public class Address {
String street;
@Embedded
Coordinates coordinates;
}
|
ColumnInfo 列信息
字段定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface ColumnInfo {
//数据库中列的名称。如果没有设置,则默认为字段名。
String name() default INHERIT_FIELD_NAME;
//列的类型关联,该列将在构造数据库时使用。
@SuppressWarnings("unused") @SQLiteTypeAffinity int typeAffinity() default UNDEFINED;
//未定义的
int UNDEFINED = 1;
//文本
int TEXT = 2;
//整数
int INTEGER = 3;
//真正的
int REAL = 4;
//二进制大对象
int BLOB = 5;
//索引字段,如果设置 true ,该字段应该被索引
boolean index() default false;
//列的排序顺序,该列将在构造数据库时使用。
@Collate int collate() default UNSPECIFIED;
//未指明的
int UNSPECIFIED = 1;
//二进制
int BINARY = 2;
//不区分大小写
int NOCASE = 3;
//清除字符串结尾空格符
int RTRIM = 4;
//局部性
@RequiresApi(21)
int LOCALIZED = 5;
//统一字符标准
@RequiresApi(21)
int UNICODE = 6;
//此列的默认值。可以通过用括号括起来来使用常量表达式。
//@ColumnInfo(defaultValue = "NULL")
//@ColumnInfo(defaultValue = "'NULL'")
//@CoumnInfo(defaultValue = "('Created at' || CURRENT_TIMESTAMP)")
String defaultValue() default VALUE_UNSPECIFIED;
//未指定的值
String VALUE_UNSPECIFIED = "[value-unspecified]";
}
|
Ignore 忽略字段
字段定义
1
2
3
4
|
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
public @interface Ignore {
}
|
NULL、CURRENT_TIMESTAMP和其他SQLite常量值被解释为这样。如果出于某种原因想将它们用作字符串,可以用单引号括起来。
1
2
3
4
|
@ColumnInfo(defaultValue = "NULL")
@ColumnInfo(defaultValue = "'NULL'")
@ColumnInfo(defaultValue = "CURRENT_TIMESTAMP")
@CoumnInfo(defaultValue = "('Created at' || CURRENT_TIMESTAMP)")
|
Relation 关系
一个方便的注释,可以在POJO中用于自动获取关系实体。当从查询中返回POJO时,它的所有关系也都由Room获取。
字段定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface Relation {
//从其中获取项的实体或视图。如果实体或视图匹配返回类型中的类型参数,则不需要设置此参数。返回要获取的实体或视图。默认情况下,从返回类型继承。
Class<?> entity() default Object.class;
//父POJO中的引用列
String parentColumn();
//要在 entity 中匹配的列
String entityColumn();
//在获取相关实体时用作关联表(也称为连接表)的实体或视图
Junction associateBy() default @Junction(Object.class);
//如果应该从实体获取子列,则可以使用此字段指定它们。默认情况下,从返回类型推断。
String[] projection() default {};
}
|
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
@Entity
public class Song {
@PrimaryKey
int songId;
int albumId;
String name;
// other fields
}
public class AlbumNameAndAllSongs {
int id;
String name;
@Relation(parentColumn = "id", entityColumn = "albumId")
List<Song> songs;
}
public class Album {
int id;
// other fields
}
public class SongNameAndId {
int songId;
String name;
}
public class AlbumAllSongs {
@Embedded
Album album;
@Relation(parentColumn = "id", entityColumn = "albumId", entity = Song.class)
List<SongNameAndId> songs;
}
@Dao
public interface MusicDao {
@Query("SELECT id, name FROM Album")
List<AlbumNameAndAllSongs> loadAlbumAndSongs();
@Query("SELECT * from Album")
List<AlbumAllSongs> loadAlbumAndSongs();
}
public class AlbumAndAllSongs {
@Embedded
Album album;
@Relation(
parentColumn = "id",
entityColumn = "albumId",
entity = Song.class,
projection = {"name"})
List<String> songNames;
}
|
Junction 连接关系
声明要用于连接关系的连接。
如果有关系应该使用关联表(也称为连接表或联接表),则可以使用此注释引用此类表。这对于获取多对多关系非常有用。
字段定义
1
2
3
4
5
6
7
8
9
10
|
@Target({})
@Retention(RetentionPolicy.CLASS)
public @interface Junction {
//在获取相关实体时用作连接表的实体或数据库视图。
Class<?> value();
//将用于匹配{@link relationship #parentColumn()}的连接列。
String parentColumn() default "";
//将用于匹配{@link relationship #entityColumn()}的连接列。
String entityColumn() default "";
}
|
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@Entity(primaryKeys = {"pId", "sId"})
public class PlaylistSongXRef {
int pId;
int sId;
}
public class PlaylistWithSongs {
@Embedded
Playlist playlist;
@Relation(
parentColumn = "playlistId",
entity = Song.class,
entityColumn = "songId",
associateBy = @Junction(
value = PlaylistSongXRef.class,
parentColumn = "pId",
entityColumn = "sId"))
List<String> songs;
}
@Dao
public interface MusicDao {
@Query("SELECT * FROM Playlist")
List<PlaylistWithSongs> getAllPlaylistsWithSongs();
}
|
ForeignKey
在另一个实体上声明外键。
字段定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
@Retention(RetentionPolicy.CLASS)
public @interface ForeignKey {
//要引用的父实体
Class<?> entity();
//父{@link实体}中的列名列表。
String[] parentColumns();
//当前{@link实体}中的列名列表。
String[] childColumns();
//从数据库中删除父{@link实体}时要采取的操作,默认NO_ACTION
@Action int onDelete() default NO_ACTION;
//被引用实体在数据库中更新时要采取的操作。默认NO_ACTION
@Action int onUpdate() default NO_ACTION;
//外键约束是否应该被延迟到事务完成。默认值为false
boolean deferred() default false;
//当从数据库中修改或删除父键时,不采取任何特殊操作。
int NO_ACTION = 1;
//限制
int RESTRICT = 2;
//置空策略
int SET_NULL = 3;
//“SET DEFAULT”操作类似于{@link #SET_NULL},只是每个子键列都被设置为包含列的默认值,而不是{@code NULL}。
int SET_DEFAULT = 4;
//“级联”操作将父键上的删除或更新操作传播到每个依赖子键。
int CASCADE = 5;
@IntDef({NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE})
@Retention(RetentionPolicy.CLASS)
@interface Action {
}
}
@ForeignKey(entity = Song.class , parentColumns = , childColumns = )
|
Index 索引
添加索引通常会加快SELECT查询的速度,但会减慢INSERT或UPDATE等其他查询的速度。在添加索引时,您应该小心,以确保这些额外的成本是值得的。
字段定义
1
2
3
4
5
6
7
8
9
10
|
@Target({})
@Retention(RetentionPolicy.CLASS)
public @interface Index {
//索引中列名的列表。
String[] value();
//索引的名称。如果没有设置,Room将把它设置为以“_”连接的列列表,并以“index_${tableName}”作为前缀。因此,如果您有一个名为“Foo”的表,并且索引为{“bar”、“baz”},则生成的索引名称将为“index_Foo_bar_baz”。如果需要在查询中指定索引,则永远不要依赖于此名称,而是为索引指定一个名称。
String name() default "";
//如果设置为true,这将是一个惟一的索引,任何副本都将被拒绝。
boolean unique() default false;
}
|
如果你喜欢我的文章,可以关注我的掘金、公众号、博客、简书或者Github!
简书: https://www.jianshu.com/u/a2591ab8eed2
GitHub: https://github.com/bugyun
Blog: https://ruoyun.vip
掘金: https://juejin.im/user/56cbef3b816dfa0059e330a8/posts
CSDN: https://blog.csdn.net/zxloveooo
欢迎关注微信公众号
