Android架构组件JetPack之Room(三)

  • Database : 持有DB和DAO
  • Entity : 定义POJO类,即数据表结构
  • DAO(Data Access Objects) : 定义访问数据(增删改查)的接口

其关系如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二、基本使用

1. 创建Entity

1.1 一个简单的Entitiy

一个简单Entity定义如下:

@Entity(tableName = “user”
indices = {@Index(value = {“first_name”, “last_name”})})
public class User {

@PrimaryKey
private int uid;

@ColumnInfo(name = “first_name”)
private String firstName;

@ColumnInfo(name = “last_name”)
private String lastName;

@Ignore
public User(String firstName, String lastName) {
this.uid = UUID.randomUUID().toString();
this.firstName = firstName;
this. lastName = lastName;
}

public User(String id, String firstName, String lastName) {
this.uid = id;
this.firstName = userName;
this. lastName = userName;
}

// Getters and setters
}

  • @Entity(tableName = "table_name**") 注解POJO类,定义数据表名称;
  • @PrimaryKey 定义主键,如果一个Entity使用的是复合主键,可以通过@Entity注解的primaryKeys 属性定义复合主键:@Entity(primaryKeys = {"firstName", "lastName"})
  • @ColumnInfo(name = “column_name”) 定义数据表中的字段名
  • @Ignore 用于告诉Room需要忽略的字段或方法
  • 建立索引:在@Entity注解的indices属性中添加索引字段。例如:indices = {@Index(value = {"first_name", "last_name"}, unique = true), ...}, unique = true可以确保表中不会出现{"first_name", "last_name"} 相同的数据。
1.2 Entitiy间的关系

不同于目前存在的大多数ORM库,Room不支持Entitiy对象间的直接引用。(具体原因可以参考: Understand why Room doesn’t allow object references
但Room允许通过**外键(Foreign Key)**来表示Entity之间的关系。

@Entity(foreignKeys = @ForeignKey(entity = User.class,
parentColumns = “id”,
childColumns = “user_id”))
class Book {
@PrimaryKey
public int bookId;

public String title;

@ColumnInfo(name = “user_id”)
public int userId;
}

如上面代码所示,Book对象与User对象是属于的关系。Book中的user_id,对应User中的id。 那么当一个User对象被删除时, 对应的Book会发生什么呢?

@ForeignKey注解中有两个属性onDeleteonUpdate, 这两个属性对应ForeignKey中的onDelete()onUpdate(), 通过这两个属性的值来设置当User对象被删除/更新时,Book对象作出的响应。这两个属性的可选值如下:

  • CASCADE:User删除时对应Book一同删除; 更新时,关联的字段一同更新
  • NO_ACTION:User删除时不做任何响应
  • RESTRICT:禁止User的删除/更新。当User删除或更新时,Sqlite会立马报错。
  • SET_NULL:当User删除时, Book中的userId会设为NULL
  • SET_DEFAULT:与SET_NULL类似,当User删除时,Book中的userId会设为默认值
1.3 对象嵌套

在某些情况下, 对于一张表中的数据我们会用多个POJO类来表示,在这种情况下可以用@Embedded注解嵌套的对象,比如:

class Address {
public String street;
public String state;
public String city;

@ColumnInfo(name = “post_code”)
public int postCode;
}

@Entity
class User {
@PrimaryKey
public int id;

public String firstName;

@Embedded
public Address address;
}

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

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

@Dao
public interface UserDao {
@Query(“SELECT * FROM user”)
List getAll();

@Query(“SELECT * FROM user WHERE uid IN (:userIds)”)
List 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);

@Insert
void insertAll(List users);

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

@Delete
void delete(User user);

@Update
public void updateUsers(List 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 getAll();

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

2.2 查询参数传递

@Query(“SELECT * FROM user WHERE uid IN (:userIds)”)
List 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 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> loadUsersFromRegionsSync(List 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 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语句的差别。

@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 findBooksBorrowedByNameSync(String userName);
    }

3. 创建数据库

Room中DataBase类似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数据库升级

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

写在最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Android核心高级技术PDF文档,BAT大厂面试真题解析】**

[外链图片转存中…(img-eQYywslM-1713266884148)]

【算法合集】

[外链图片转存中…(img-ReZa9tJJ-1713266884149)]

【延伸Android必备知识点】

[外链图片转存中…(img-5AtuOnXg-1713266884150)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值