要使用Room持久性库访问应用程序的数据,您需要使用数据访问对象或DAO。 这组Dao对象构成了Room的主要组件,因为每个DAO都包含提供对应用程序数据库的抽象访问的方法。
通过使用DAO类而不是查询构建器或直接查询来访问数据库,您可以分离数据库体系结构的不同组件。 此外,DAO允许您在测试应用程序时轻松模拟数据库访问。
DAO可以是接口,也可以是抽象类。 如果它是一个抽象类,它可以选择有一个构造函数,它将RoomDatabase作为唯一参数。 Room在编译时创建每个DAO实现。
注意:除非您在构建器上调用
allowMainThreadQueries()
,否则Room不支持主线程上的数据库访问,因为它可能会长时间锁定UI。 异步查询 - 返回LiveData或Flowable实例的查询 - 不受此规则的约束,因为它们在需要时在后台线程上异步运行查询。
一、为方便起见定义方法
您可以使用DAO类表示多个便捷查询。 本文档包含几个常见示例。
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
方法只接收1个参数,则它可以返回long,这是插入项的新rowId
。 如果参数是数组或集合,则应返回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会以下列两种方式之一提醒您:
- 如果只有一些字段名称匹配,它会发出警告。
- 如果没有字段名称匹配,则会出错。
简单的查询
@Dao
public interface MyDao {
@Query("SELECT * FROM user")
public User[] loadAllUsers();
}
这是一个非常简单的查询,可以加载所有用户。 在编译时,Room知道它正在查询用户表中的所有列。 如果查询包含语法错误,或者数据库中不存在用户表,则在应用程序编译时,Room会显示包含相应消息的错误。
将参数传递给查询
大多数情况下,您需要将参数传递给查询以执行过滤操作,例如仅显示年龄超过特定年龄的用户。 要完成此任务,请在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可能只显示用户的名字和姓氏,而不是每个用户的详细信息。 通过仅提取应用程序UI中显示的列,可以节省宝贵的资源,并且您的查询可以更快地完成。
Room允许您从查询中返回任何基于Java的对象,只要结果列集可以映射到返回的对象即可。 例如,您可以创建以下普通的基于Java的旧对象(POJO)来获取用户的名字和姓氏:
public class NameTuple {
@ColumnInfo(name="first_name")
public String firstName;
@ColumnInfo(name="last_name")
public String lastName;
}
现在,您可以在查询方法中使用此POJO:
@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会显示警告。
注意:这些POJO也可以使用@Embedded批注。
传递一组参数
您的一些查询可能要求您传入可变数量的参数,并且在运行时之前不知道参数的确切数量。 例如,您可能希望从区域子集中检索有关所有用户的信息。 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);
}
注意:从版本1.0开始,Room使用查询中访问的表列表来决定是否更新LiveData的实例。
使用RxJava进行反应性查询
Room还可以从您定义的查询中返回RxJava2 Publisher
和Flowable
对象。 要使用此功能,请将Room组中的android.arch.persistence.room:rxjava2
工件添加到构建Gradle依赖项中。 然后,您可以返回RxJava2中定义的类型的对象,如以下代码段所示:
@Dao
public interface MyDao {
@Query("SELECT * from user where id = :id LIMIT 1")
public Flowable<User> loadUserById(int id);
}
直接光标访问
如果您的应用程序逻辑需要直接访问返回行,则可以从查询中返回Cursor对象,如以下代码段所示:
@Dao
public interface MyDao {
@Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
public Cursor loadRawUsersOlderThan(int minAge);
}
警告:使用Cursor API非常不鼓励,因为它不保证行是否存在或行包含的值。 仅当您已经拥有需要光标的代码且无法轻松重构时才使用此功能。
查询多个表
您的某些查询可能需要访问多个表来计算结果。 Room允许您编写任何查询,因此您也可以连接表。 此外,如果响应是可观察的数据类型(如Flowable或LiveData),则会监视查询中引用的所有表以进行无效。
以下代码段显示了如何执行表连接以合并包含借书用户的表和包含当前借阅书籍数据的表之间的信息:
@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;
}
}
网友评论