Room系列专题

Android Jetpack之Room篇

Room Entity注解说明

Room Dao注解说明

Room Fts 虚拟表模块

Room DatabaseView 视图

Room SkipQueryVerification

Room TypeConverter 属性类型转换器

先导

img

我们先看一个例子,通过这个例子来查看每个注解具体的含义

 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 {};
}
  1. 这个类在数据库中有一个映射SQLite表。
  2. 每个实体必须至少有一个用 PrimaryKey注释的字段。
  3. 每个实体必须要么有一个无参数构造函数,要么有一个参数匹配字段的构造函数(基于类型和名称)。构造函数不必接收所有字段作为参数,但是如果没有将字段传递给构造函数,那么它要么是公共的,要么有一个公共setter。如果有匹配的构造函数可用,Room将始终使用它。如果不希望它使用构造函数,可以使用 Ignore注释它。
  4. 当一个类被标记为一个实体时,它的所有字段都被持久化。如果您想排除它的一些字段,您可以用 Ignore标记它们。
  5. 如果字段是 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;
}
  1. 单一主键,如果想创建多个主键,那么可以通过**@Entity(primaryKeys= [])**在类上定义。
  2. 每个bean类都必须要声明一个主键,除非父类声明了。
  3. 如果这个属性被**@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  "";
}
  1. 如果加上这个字段就会创建多个列,列名就是自定好的属性字段。
  2. 如果你想查询Coordinates 类的 2 个属性,那么就会返回这个类。
  3. 如果与子对象和所有者对象的字段存在名称冲突,则可以为子对象的字段指定 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

欢迎关注微信公众号