Room系列专题

Android Jetpack之Room篇

Room Entity注解说明

Room Dao注解说明

Room Fts 虚拟表模块

Room DatabaseView 视图

Room SkipQueryVerification

Room TypeConverter 属性类型转换器

定义

我们先看一个列子,然后通过这个例子一步一步的讲解关于 Dao 的注解的含义。

例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Dao
public interface UserDao {
    @Query("SELECT * FROM Users LIMIT 1")
    Flowable<User> getUser();

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    Completable insertUser(User user);

    @Query("DELETE * FROM Users")
    void deleteAllUsers();
    
    @Query("SELECT * FROM Users WHERE userid = :userId")
    public abstract List<User> findUserById(int userId);
}

注解说明

Dao 字段

1
2
3
4
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Dao {
}

@Dao 将类标记为数据访问对象。主要有以下操作符:

  • Query
  • Delete
  • Insert

Query 查询操作

字段说明

1
2
3
4
5
6
7
8
9
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Query {
    /
      The SQLite query to be run.
      @return The query to be run.
     /
    String value();
}

通过 :value 匹配单参数,用一个列子来展示如何使用:

1
2
@Query("SELECT * FROM Users WHERE userid = :userId")
public abstract List<User> findUserById(int userId);

作为SQLite绑定参数的扩展,Room支持将参数列表绑定到查询。在运行时,Room将构建正确的查询,以根据方法参数中的项数匹配绑定参数的数量。

通过IN(:value)来匹配数组

1
2
@Query("SELECT * FROM Users WHERE userid IN(:userIds)")
public abstract List<User> findUserById(int[] userIds);

通过IN(?, ?, ?)来匹配多个参数

1
2
@Query("SELECT * FROM Users WHERE userid IN(?, ?, ?)")
public abstract List<User> findUserById(int userId1,int userId2,int userId3); 

例子,查找一个对象

1
2
3
4
5
6
7
class SongDuration {
    String name;
    @ColumnInfo(name = "duration")
    String length;
}
@Query("SELECT name, duration FROM song WHERE id = :songId LIMIT 1")
public abstract SongDuration findSongDuration(int songId);

注意

Query方法支持4种类型的语句:SELECTINSERTUPDATEDELETE

返回值说明:

  1. 对于SELECT查询,Room将从方法的返回类型推断结果内容,并生成代码,该代码将自动将查询结果转换为方法的返回类型。对于单个结果查询,返回类型可以是任何数据对象(也称为pojo)。对于返回多个值的查询,可以使用listArray。此外,任何查询都可能返回 Cursor 或任何查询结果都可以封装在 LiveData。具体请查看下面文章。
  2. INSERT查询可以返回 voidlong。如果它是一个 long,那么它的值就是这个查询插入的行的SQLite rowid。注意,插入多行的查询不能返回一个以上的rowid,所以如果返回long,请避免这样的语句。
  3. UPDATEDELETE查询可以返回 voidint。如果是 int,则值是受此查询影响的行数
  4. 如果使用RxJava2,还可以从查询方法返回FlowablePublisher。由于反应性流不允许 null,如果查询返回一个可空类型,那么如果值为 null 它将不会分派任何内容(比如获取一个不存在的Entity行)。您可以返回Flowable<T[]>Flowable<List>来解决这个限制。Flowable Publisher都将观察数据库的更改,如果数据发生更改,则重新调度。如果希望查询数据库而不观察更改,可以使用MaybeSingle。如果 Single查询返回 null, Room将抛出 EmptyResultSetException。此外,如果语句是插入、更新或删除语句,则支持返回类型 SingleMaybeCompletable。只要POJO的字段与查询结果中的列名匹配,就可以从查询方法返回任意POJO。

Delete 删除操作

含义:从数据库中删除其参数

字段的定义

1
2
3
4
5
6
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Delete {
    //删除方法的目标实体,默认情况下,目标实体由方法参数解释。
    Class<?> entity() default Object.class;
}

例子

1
2
3
4
5
@Dao
public interface UserDao { 
    @Delete //根据PrimaryKey 来进行删除
    public abstract void deleteUser(User ...user);
}

Insert 插入方法

字段的定义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface Insert {
    //插入方法的目标实体,默认情况下,目标实体由方法参数解释。
    Class<?> entity() default Object.class;
    //如果发生冲突该怎么办?
    //使用 ABORT (默认)回滚冲突事务。
    //使用 REPLACE 用新行替换现有行。
    //使用 IGNORE 保存现有行。
    @OnConflictStrategy
    int onConflict() default OnConflictStrategy.ABORT;
}

例子,插入一个对象,如果存在就替换或更新

1
2
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertUser(User user);

Update

字段的定义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Retention(RetentionPolicy.CLASS)
public @interface Update {
    //更新方法的目标实体,默认情况下,目标实体由方法参数解释。
    Class<?> entity() default Object.class;
    //如果发生冲突该怎么办?
    //使用 ABORT (默认)回滚冲突事务。
    //使用 REPLACE 用新行替换现有行。
    //使用 IGNORE 保存现有行。
    @OnConflictStrategy
    int onConflict() default OnConflictStrategy.ABORT;
}

例子,更新对象和更新多个对象

1
2
3
4
5
6
7
@Dao
public interface UserDao {
    @Update
    void updateUser(User user);
    @Update
    public int updateUsers(List<User> users);
}

Transaction 事务

字段的定义

1
2
3
4
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface Transaction {
}

例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Dao
public interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertUser(User user);
    @Delete //根据PrimaryKey 来进行删除
    void deleteUser(User ...user);
    //操作插入新的,删除旧的
    public void caoTransaction(User newUser, User oldUser){
        insertUser(newUser);
        deleteUser(oldUser);
    }
}

注意:

当在具有 SELECT语句的 Query方法上使用时,此查询生成的代码将在事务中运行。

有两种主要的情况,你可能想要这样做:

如果查询的结果相当大,那么最好在事务中运行它,来接收一致的结果。否则,如果查询结果不适合单个数据操作,由于游标窗口交换之间数据库中的更改,查询结果可能被损坏。

如果查询的结果是一个带有relationship字段的POJO,则分别查询这些字段。要在这些查询之间接收一致的结果,还需要在单个事务中运行它们。

如果查询是异步的,例如,返回lifecycle。或者RxJava Flowable,在运行查询时正确处理事务,而不是在调用方法时。

将此注释放在 InsertUpdateDelete方法上没有影响,因为这些方法总是在事务中运行。类似地,如果一个方法使用Query注释,但是运行INSERTUPDATEDELETE语句,那么它将自动包装在一个事务中,而这个注释没有效果。

Room一次最多只执行一个事务,其他事务按先到先得顺序排队执行

RawQuery 原始查询

将 Dao 注释类中的方法标记为原始查询方法,您可以将SupportSQLiteQuery 字段作为查询传递

字段定义

1
2
3
4
5
6
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface RawQuery {
    //实体列表
    Class<?>[] observedEntities() default {};
}

例子

查询 Song,添加@RawQuery 注解,参数使用 SupportSQLiteQuery来传递想要传递的参数。

1
2
3
4
5
6
7
8
9
@Dao
interface RawDao {
    @RawQuery
    Song getSongViaQuery(SupportSQLiteQuery query);
}

// Usage of RawDao
SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT  FROM Song WHERE id = ? LIMIT 1", new Object[]{songId});
Song song = rawDao.getSongViaQuery(query);

如果你喜欢我的文章,可以关注我的掘金、公众号、博客、简书或者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

欢迎关注微信公众号