写点什么

Android 架构组件 JetPack 之 Room(三),Android 大厂 74 道高级面试合集

用户头像
Android架构
关注
发布于: 刚刚

以上代码所产生的 User 表中,Column 为id, firstName, street, state, city, post_code

2. 创建数据访问对象(DAO)

@Daopublic interface UserDao {@Query("SELECT * FROM user")List<User> getAll();


@Query("SELECT * FROM user WHERE uid IN (:userIds)")List<User> loadAllByIds(int[] userIds);


@Query("SELECT * FROM user WHERE first_name LIKE :first AND "


  • "last_name LIKE :last LIMIT 1")User findByName(String first, String last);


@Insertvoid insertAll(List<User> users);


@Insert(onConflict = OnConflictStrategy.REPLACE)public void insertUsers(User... users);


@Deletevoid delete(User user);


@Updatepublic void updateUsers(List<User> users);}


DAO 可以是一个接口,也可以是一个抽象类, Room 会在编译时创建 DAO 的实现。


Tips:


  • @Insert方法也可以定义返回值, 当传入参数仅有一个时返回long, 传入多个时返回long[]List<Long>, Room 在实现 insert 方法的实现时会在一个事务进行所有参数的插入。

  • @Insert的参数存在冲突时, 可以设置onConflict属性的值来定义冲突的解决策略, 比如代码中定义的是@Insert(onConflict = OnConflictStrategy.REPLACE), 即发生冲突时替换原有数据

  • @Update@Delete 可以定义int类型返回值,指更新/删除的函数


DAO 中的增删改方法的定义都比较简单,这里不展开讨论,下面更多的聊一下查询方法。

2.1 简单的查询

Talk is cheap, 直接 show code:


@Query("SELECT * FROM user")List<User> getAll();


Room 会在编译时校验 sql 语句,如果@Query() 中的 sql 语句存在语法错误,或者查询的表不存在,Room 会在编译时报错。

2.2 查询参数传递

@Query("SELECT * FROM user WHERE uid IN (:userIds)")List<User> loadAllByIds(int[] userIds);


@Query("SELECT * FROM user WHERE first_name LIKE :first AND "


  • "last_name LIKE :last LIMIT 1")User findByName(String first, String last);


看代码应该比较好理解, 方法中传递参数arg, 在 sql 语句中用:arg即可。编译时 Room 会匹配对应的参数。


如果在传参中没有匹配到:arg对应的参数, Room 会在编译时报错。

2.3 查询表中部分字段的信息

在实际某个业务场景中, 我们可能仅关心一个表部分字段的值,这时我仅需要查询关心的列即可。


定义子集的 POJO 类:


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


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


在 DAO 中添加查询方法:


@Query("SELECT first_name, last_name FROM user")public List<NameTuple> loadFullName();


这里定义的 POJO 也支持使用@Embedded

2.3 查询结果的返回类型

Room 中查询操作除了返回 POJO 对象及其 List 以外, 还支持:


  • LiveData<T>:LiveData 是架构组件库中提供的另一个组件,可以很好满足数据变化驱动 UI 刷新的需求。Room 会实现更新 LiveData 的代码。


@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")public LiveData<List<User>> loadUsersFromRegionsSync(List<String> regions);


  • Flowablbe<T> Maybe<T> Single<T>:Room 支持返回 RxJava2 的Flowablbe, MaybeSingle对象,对于使用 RxJava 的项目可以很好的衔接, 但需要在 gradle 添加该依赖:android.arch.persistence.room:rxjava2


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


  • Cursor:返回 Cursor 是为了支持现有项目中使用 Cursor 的场景,官方不建议直接返回 Cursor.


Caution: It's highly discouraged to work with the Cursor API because it doesn't guarantee whether the rows exist or what values the rows contain. Use this functionality only if you already have code that expects a cursor and that you can't refactor easily.

2.4 联表查询

Room 支持联表查询,接口定义上与其他查询差别不大, 主要还是 sql 语句的差别。


@Daopublic 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);}

3. 创建数据库

Room 中 DataB


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ase 类似 SQLite API 中 SQLiteOpenHelper,是提供 DB 操作的切入点,但是除了持有 DB 外, 它还负责持有相关数据表(Entity)的数据访问对象(DAO), 所以 Room 中定义 Database 需要满足三个条件:


  • 继承 RoomDataBase,并且是一个抽象类

  • 用 @Database 注解,并定义相关的 entity 对象, 当然还有必不可少的数据库版本信息

  • 定义返回 DAO 对象的抽象方法


@Database(entities = {User.class}, version = 1)public abstract class AppDatabase extends RoomDatabase {public abstract UserDao userDao();}


创建好以上 Room 的三大组件后, 在代码中就可以通过以下代码创建 Database 实例。


AppDatabase db = Room.databaseBuilder(getApplicationContext(),AppDatabase.class, "database-name").build();

三、数据库迁移

3.1 Room 数据库升级

在传统的 SQLite API 中,我们如果要升级数据库, 通常在SQLiteOpenHelper.onUpgrade方法执行数据库升级的 sql 语句,这些 sql 语句的通常根据数据库版本以文件的方式或者用数组来管理。有人说这种方式升级数据库就像在拆炸弹,相比之下在 Room 中升级数据库简单的就像是按一个开关而已。


Room 提供了 Migration 类来实现数据库的升级:


Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name").addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();


static final Migration MIGRATION_1_2 = new Migration(1, 2) {@Overridepublic void migrate(SupportSQLiteDatabase database) {database.execSQL("CREATE TABLE Fruit (id INTEGER, "


  • "name TEXT, PRIMARY KEY(id))");}};


static final Migration MIGRATION_2_3 = new Migration(2, 3) {@Overridepublic void migrate(SupportSQLiteDatabase database) {database.execSQL("ALTER TABLE Book "


  • " ADD COLUMN pub_year INTEGER");}};


在创建 Migration 类时需要指定startVersionendVersion, 代码中MIGRATION_1_2MIGRATION_2_3的 startVersion 和 endVersion 是递增的, Migration 其实是支持从版本 1 直接升到版本 3,只要其migrate()方法里执行的语句正常即可。那么 Room 是怎么实现数据库升级的呢?其实本质上还是调用SQLiteOpenHelper.onUpgrade,Room 中自己实现了一个SQLiteOpenHelper, 在onUpgrade()方法被调用时触发Migration,当第一次访问数据库时,Room 做了以下几件事:


  • 创建 Room Database 实例

  • SQLiteOpenHelper.onUpgrade被调用,并且触发Migration

  • 打开数据库


这样一看, Room 中处理数据库升级确实很像是加一个开关。

3.2 原有 SQLite 数据库迁移至 Room

因为 Room 使用的也是 SQLite, 所以可以很好的支持原有 Sqlite 数据库迁移到 Room。


假设原有一个版本号为 1 的数据库有一张表 User, 现在要迁移到 Room, 我们需要定义好 Entity, DAO, Database, 然后创建 Database 时添加一个空实现的 Migraton 即可。需要注意的是,即使对数据库没有任何升级操作,也需要升级版本, 否则会抛异常IllegalStateException.


@Database(entities = {User.class}, version = 2)public abstract class UsersDatabase extends RoomDatabase {…static final Migration MIGRATION_1_2 = new Migration(1, 2) {@Overridepublic void migrate(SupportSQLiteDatabase database) {// Since we didn't alter the table, there's nothing else to do here.}};…database = Room.databaseBuilder(context.getApplicationContext(),UsersDatabase.class, "Sample.db").addMigrations(MIGRATION_1_2).build();

四、复杂数据的处理

在某些场景下我们的应用可能需要存储复杂的数据类型,比如Date,但是 Room 的 Entity 仅支持基本数据类型和其装箱类之间的转换,不支持其它的对象引用。所以 Room 提供了TypeConverter给使用者自己实现对应的转换。


一个Date类型的转换如下:


public class Converters {@TypeConverterpublic static Date fromTimestamp(Long value) {return value == null ? null : new Date(value);}


@TypeConverterpublic static Long dateToTimestamp(Date date) {return date == null ? null : date.getTime();}

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android架构组件JetPack之Room(三),Android大厂74道高级面试合集