Android Architecture Component Room持久化数据库(四) 使用Room DAO访问数据

本文深入讲解了使用Android Room持久化库进行数据访问的方法,包括数据访问对象(DAO)的定义,以及如何通过DAO进行数据的插入、更新、删除和查询操作。同时,介绍了如何使用LiveData和RxJava进行数据的实时监听。

版权声明:本文为博主原创文章,欢迎大家转载!

但是转载请标明出处: https://blog.youkuaiyun.com/t000818/article/details/84290435 ,本文出自:【唐宏宇的博客】


要使用Room persistence library访问应用程序的数据,您需要使用数据访问对象或DAO。这组Dao对象构成了Room的主要组件,每个DAO都包含了访问数据库的抽象方法,提供给App使用。

通过使用DAO类而不是查询构造器或直接查询来访问数据库,可以与数据库架构解耦。此外,DAO允许你在测试应用程序时,轻松模拟数据库访问。

注意:在将DAO类添加到应用程序之前,请将Architecture Components添加到应用程序的build.gradle文件中。

DAO可以是接口,也可以是抽象类。如果它是一个抽象类,它可以选择有一个构造函数,它将RoomDatabase作为唯一参数。 Room在编译期间创建每个DAO实例。

注意:除非在构建器上调用allowMainThreadQueries() ,否则Room不支持主线程上的数据库访问,因为它可能会长时间锁定UI。而异步查询 - 返回LiveData 或 Flowable实例的查询 - 不受此规则的约束,因为它们只有在需要时,会后台线程上异步运行查询。

 

定义快捷操作方法

可以使用DAO类表示多个快捷查询。本文档包含几个常见示例。

插入


当您创建DAO方法并使用@Insert注解它时,Room会生成一个实现,该实现在单个事务中将所有参数插入到数据库中。

以下代码段显示了几个查询示例:

Kotlin 实现:

@Dao
interface MyDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUsers(vararg users: User)

    @Insert
    fun insertBothUsers(user1: User, user2: User)

    @Insert
    fun insertUsersAndFriends(user: User, friends: List<User>)
}

Java实现:

@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>。

有关更多详细信息,请参阅@Insert注解的参考文档,以及rowid表的 SQLite文档。

 

更新

Update 快捷方法会根据方法参数中给出的一组实体,修改数据库,它使用与每个实体的主键来匹配查询并更新数据。
以下代码段演示了如何定义此方法:

Kotlin实现:

@Dao
interface MyDao {
    @Update
    fun updateUsers(vararg users: User)
}

Java实现:

@Dao
public interface MyDao {
    @Update
    public void updateUsers(User... users);
}

虽然通常没有必要,但可以让此方法返回一个int值,表示数据库中更新的行数。

 

删除

Delete快捷方法会根据方法参数中给出的一组实体,在数据库中对应删除。它使用主键来查找要删除的实体。

以下代码段演示了如何定义此方法:

Kotlin实现:

@Dao
interface MyDao {
    @Delete
    fun deleteUsers(vararg users: User)
}

Java实现:

@Dao
public interface MyDao {
    @Delete
    public void deleteUsers(User... users);
}

虽然通常没有必要,但可以让此方法返回一个int值,表示数据库中删除的行数。

 

查询信息


@Query是DAO类中使用的主要注解。它允许对数据库执行读/写操作。每个@Query 查询方法在编译时验证,因此如果查询出现问题,则会发生编译错误而不是运行时失败。

Room还会验证查询的返回值,如果返回的对象中的字段名称与查询数据库返回的相应列名称不匹配,则Room会以下列两种方式之一提醒您:

  • 如果只有一些字段名称匹配,它会发出警告。
  • 如果没有字段名称匹配,则会出错。

简单查询

Kotlin实现:

@Dao
interface MyDao {
    @Query("SELECT * FROM user")
    fun loadAllUsers(): Array<User>
}

Java实现:

@Dao
public interface MyDao {
    @Query("SELECT * FROM user")
    public User[] loadAllUsers();
}

这是一个非常简单的查询,可以加载所有用户。在编译时,Room知道它正在查询用户表中的所有列。如果查询包含语法错误,或者数据库中不存在用户表,则当应用程序编译时,Room会显示错误并显示相应的消息

在查询中添加参数

大多数情况下,需要将参数传递给查询以执行过滤操作,例如仅显示年龄超过特定年龄的用户。要完成此任务,请在Room注解中使用方法参数,如以下代码段所示:

Kotlin写法:

@Dao
interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge")
    fun loadAllUsersOlderThan(minAge: Int): Array<User>
}

Java写法:

@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge")
    public User[] loadAllUsersOlderThan(int minAge);
}

在编译时处理此查询时,Room会将:minAge绑定参数与minAge方法参数进行匹配。 Room使用参数名称执行匹配。
如果存在不匹配,则会在应用编译时会发生错误。

你还可以在查询中传递多个参数或多次引用它们,如以下代码段所示:

Kotlin写法:

@Dao
interface MyDao {
    @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
    fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User>

    @Query("SELECT * FROM user WHERE first_name LIKE :search " +
           "OR last_name LIKE :search")
    fun findUserWithName(search: String): List<User>
}

Java写法:

@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的旧对象(PO​​JO)来获取用户的名字和姓氏:

Kotlin写法:

data class NameTuple(
    @ColumnInfo(name = "first_name") var firstName: String?,
    @ColumnInfo(name = "last_name") var lastName: String?
)

Java写法:

public class NameTuple {
    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;
}

下面就可以在查询方法中使用此POJO

Kotlin写法:

@Dao
interface MyDao {
    @Query("SELECT first_name, last_name FROM user")
    fun loadFullName(): List<NameTuple>
}

Java写法:

@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理解参数何时表示集合,并根据提供的参数数量在运行时自动扩展它。

Kotlin写法:

@Dao
interface MyDao {
    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
    fun loadUsersFromRegions(regions: List<String>): List<NameTuple>
}

Java写法:

@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。

Kotlin写法:

@Dao
interface MyDao {
    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
    fun loadUsersFromRegionsSync(regions: List<String>): LiveData<List<User>>
}

Java写法:

@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类型的返回值提供以下支持:


要使用此功能,请在应用程序的build.gradle文件中包含最新版本的rxjava2依赖库:

以下代码段显示了如何使用这些返回类型的几个示例:

Kotlin写法:

@Dao
interface MyDao {
    @Query("SELECT * from user where id = :id LIMIT 1")
    fun loadUserById(id: Int): Flowable<User>

    // Emits the number of users added to the database.
    @Insert
    fun insertLargeNumberOfUsers(users: List<User>): Maybe<Int>

    // Makes sure that the operation finishes successfully.
    @Insert
    fun insertLargeNumberOfUsers(varargs users: User): Completable

    /* Emits the number of users removed from the database. Always emits at
       least one user. */
    @Delete
    fun deleteAllUsers(users: List<User>): Single<Int>
}

Java写法:

@Dao
public interface MyDao {
    @Query("SELECT * from user where id = :id LIMIT 1")
    public Flowable<User> loadUserById(int id);

    // Emits the number of users added to the database.
    @Insert
    public Maybe<Integer> insertLargeNumberOfUsers(List<User> users);

    // Makes sure that the operation finishes successfully.
    @Insert
    public Completable insertLargeNumberOfUsers(User... users);

    /* Emits the number of users removed from the database. Always emits at
       least one user. */
    @Delete
    public Single<Integer> deleteUsers(List<User> users);
}

有关更多详细信息,请参阅Google Developers  Room and RxJava 文章

直接Cursor访问
如果应用程序逻辑需要直接访问返回行项数据,则可以从查询中返回Cursor对象,如以下代码段所示:

Kotlin写法:

@Dao
interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    fun loadRawUsersOlderThan(minAge: Int): Cursor
}

Java写法:

@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    public Cursor loadRawUsersOlderThan(int minAge);
}

警告:Google非常不建议使用Cursor API,因为它不保证行是否存在或行包含的值。仅当已经拥有需要光标的代码且无法轻松重构时才使用此功能。

 

查询多张表

某些查询可能需要访问多个表来计算结果。 Room可以让我们编写任何查询,因此也可以连接表。此外,如果响应是可观察的数据类型(如Flowable 或 LiveData),则会监视查询中引用的所有表以使其无效化。

以下代码段显示了如何执行表连接,以合并包含借书用户的表和包含当前借阅书籍数据的表之间的信息:

Kotlin写法:

@Dao
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"
    )
    fun findBooksBorrowedByNameSync(userName: String): List<Book>
}

Java写法:

@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。例如,您可以编写一个查询来加载用户及其宠物的名称,如下所示:

Kotlin写法:

@Dao
interface MyDao {
    @Query(
        "SELECT user.name AS userName, pet.name AS petName " +
        "FROM user, pet " +
        "WHERE user.id = pet.user_id"
    )
    fun loadUserAndPetNames(): LiveData<List<UserPet>>

    // You can also define this class in a separate file.
    data class UserPet(var userName: String?, var petName: String?)
}

Java写法:

@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();

   // 你也可以将该类定义成一个单独的类文件且申明为public
   static class UserPet {
       public String userName;
       public String petName;
   }
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值