源文档链接
数据调用
可以使用DAO来查询存储数据。这一些列的DAO使用Room的重要组件,每个DAO都提供抽象的数据操作方法
使用DAO操作时,不使用查询构建者或者直接的查询,可以根据自己的数据库结构划分不同的组件。更多的,DAO允许模拟数据库来测试
使用
@DAO
定义
在添加DAO之前,先将配置添加到build.gradle中
DAO可以是接口或者抽象类。如果是个抽象类,可以拥有仅包含RoomDatabase
的构造器。Room在编译期实现每个DAO
Room不允许在主线程使用,除非调用了
allowMainThreadQueries
,因为可能锁的问题使得UI无响应。
简单方法定义
–
Room包括很多方便的查询,这里对常见查询举例
Insert
当定义一个DAO方法并使用@Insert
注解时,Room会生成实现代码,并在一个事务中间所有的参数插入数据库
例:
@Dao
public interface MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertUsers(User... users);
@Insert
public void insertBothUsers(User user1, User user2);
@Insert
public void insertUsersAndFriends(User user, List<User> friends);
}
如果@Insert
方法只接受一个参数,可以返回一个代表新插入数据的rowId的long
类型值。如果参数是个数组或者集合,会同样的返回long[]
或者List<Long>
Update
Update
方法会重新定义表中对应参数中的一系列实体。使用主键来查询每个实体
@Dao
public interface MyDao {
@Update
public void updateUsers(User... users);
}
虽然不是必须的,这个方法可以返回int
类型的值,标明表中有多少条数据更新
Delete
Delete
方法会移除方法中的一系列实体。使用主键来查询并且移除
@Dao
public interface MyDao {
@Delete
public void deleteUsers(User... users);
}
同样不是必须的,这个方法可以返回int
类型的值,表明移除了多少条数据
查询信息
–
Query
是DAO类中使用的主要注解。允许自定义对表进行读写操作。每个@Query
方法会在编译器进行验证,避免在运行时出错
Room也验证了查询的返回值,如果返回对象中的属性和表中的字段名不匹配是,Room会通过两种方式提示:
- 如果只有一些属性名匹配,会发出warning
- 如果没有属性名匹配,会发出error
简单查询
@Dao
public interface MyDao {
@Query("SELECT * FROM user")
public User[] loadAllUsers();
}
这是一个非常简单所有user的查询,在编译器,Room知道会查询user表中的所有列。如果查询中包括了符号错误,或者是user表根本不存在,Room会在app编译时提示异常
参数占位
大多数情况,sql语句应该是动态的,Room允许使用占位符,并使用方法的参数进行填充
@Dao
public interface MyDao {
@Query("SELECT * FROM user WHERE age > :minAge")
public User[] loadAllUsersOlderThan(int minAge);
}
编译器,Room会使用参数中的minAge
来绑定:minAge
值。如果这里两个名称不匹配,Room会报错
多个参数
@Dao
public interface MyDao {
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
public User[] loadAllUsersBetweenAges(int minAge, int maxAge);
@Query("SELECT * FROM user WHERE first_name LIKE :search "
+ "OR last_name LIKE :search")
public List<User> findUserWithName(String search);
}
返回列的子集
大多数情况,仅仅需要获取实体的一部分属性。例如,UI中可能只是展示first name和last name,而不是user的所有信息。可以只保存有价值的信息,还能加快查询速度
Room允许查询时返回任意可以将查询列结果包装的java对象,可以使用POJO取用户的first name和last name
public class NameTuple {
@ColumnInfo(name="first_name")
public String firstName;
@ColumnInfo(name="last_name")
public String lastName;
}
作为查询的返回
@Dao
public interface MyDao {
@Query("SELECT first_name, last_name FROM user")
public List<NameTuple> loadFullName();
}
Room能够理解返回first_name
和last_name
的值,并填充到NameTuple
对象中。因此,Room可以生成合适的代码。如果查询的返回具有更多的列,或者某个列不再NameTuple
中,Room会提示warning
这些POJO也可以使用
@Embedded
注解
使用集合声明
一些查询可能需要一组参数,而且运行时不确定个数。例如:想获取所有users中一个地区的子集。Room允许使用集合参数,并在运行时按照内容个数将集合展开
@Dao
public interface MyDao {
@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
public List<NameTuple> loadUsersFromRegions(List<String> regions);
}
可观察的查询
查询时,UI可能需要根据数据的改变而改变。为此,提供了LiveData的返回类型。Room生成了必须的代码保证数据库更新时更新LiveData
@Dao
public interface MyDao {
@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
public LiveData<List<User>> loadUsersFromRegionsSync(List<String> regions);
}
Note:1.0版本,Room使用查询中表的列表来决定是否更新LiveData
响应式查询 – Rxjava
Room可以返回Rxjava2
的Publisher
和Flowable
对象,需要添加android.arch.persistence.room:rxjava2
的依赖
@Dao
public interface MyDao {
@Query("SELECT * from user where id = :id LIMIT 1")
public Flowable<User> loadUserById(int id);
}
直接使用游标
@Dao
public interface MyDao {
@Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
public Cursor loadRawUsersOlderThan(int minAge);
}
注意:强烈不建议使用Cursor API,因为无法保证行是否存在或者行中包含的值。
多表联查
Room允许使用任意的查询语句,包括join
。如果是观察式查询,如返回值为Flowable或者LiveData,Room会监视查询中涉及的所有表
@Dao
public interface MyDao {
@Query("SELECT * FROM book "
+ "INNER JOIN loan ON loan.book_id = book.id "
+ "INNER JOIN user ON user.id = loan.user_id "
+ "WHERE user.name LIKE :userName")
public List<Book> findBooksBorrowedByNameSync(String userName);
}
也可以返回POJO
@Dao
public interface MyDao {
@Query("SELECT user.name AS userName, pet.name AS petName "
+ "FROM user, pet "
+ "WHERE user.id = pet.user_id")
public LiveData<List<UserPet>> loadUserAndPetNames();
// You can also define this class in a separate file, as long as you add the
// "public" access modifier.
static class UserPet {
public String userName;
public String petName;
}
}