本文目的在于,针对room原生api中有一些不方便的地方,基于注解与RxJava,kotlin的强大lambda表达式进行包装节省大量开发工作。
谷歌发布了ROOM数据库,与其他常见数据库api的区别是,基于注解与sql,不需要像其他库一样去记数据库内置的DAO的各种操作api函数,数据库迁移功能很强大而且逻辑清晰,便于维护。官方支持RxJava,对于查询结果可以很方便的实现异步订阅。
Room提供给我们的是一个基础的有无限可能的库,也许对于新手程序员来说,不如greendao等库用起来足够傻瓜式(只需要记住增删改查的api,不需要自己去写dao的sql语句),但是谷歌的库都有一个特点,就是基于你的能力,可以有无限的扩展可能(databinding库也是类似,新手觉得没有便捷多少,但是如果你自己积累,编写了很多自定义xml属性,和自己改造包装通用的工具方法,databinding甚至可以让给你在xml中一行属性完成listView的adapter从创建,填充数据一条龙服务,这点稍后会把我的基于databinding的包装心得写一篇文章,抛砖引玉,启发大家想出天马行空的使用方式)。关于数据库的使用不做过于详细的讲解,我这里只对于从0开始组建room数据库实践过程中的心得,如何利用。
关于ROOM的痛点:
1.对比与其他的开源数据库,在创建dao的时候,需要手动写增删该查,如果表很多,也有不小的工作量。
2.不进行包装,原生使用方式基于Dao对象操作,对于增删改查都要写大量的rxJava的订阅监听以及线程的模版代码,这些都可以通过技术手段简化,避免。
github地址:
一.下面我们来看一下原生的Room使用过程(归结为:Entity,Dao,DataBase三大对象):
1.对数据对象进行@entity注解处理,转化为Entity类:比较简单,class上标记@Entity,对于主键变量,标记@PrimaryKey
2.根据Entity对象,创建对应对DAO类,通过注解的方式,将SQL(不用害怕敲sql,用room库的话,在敲sql的时候也是有代码提示的,不会像写纯string一样出现笔误等情况。)语句注册给对应对操作方法:
3. 创建自己的DataBase类(继承RoomDatabase),声明你之前创建的dao的操作方法
4.以查询方法为例子,api的实际调用:获取数据库实例,获取dao实例,操作dao的方法。
二.本文的包装思路与实际操作预览:
下面是我的包装方法:首先解决不必要重复写的增删等方法。然后已一个DataBaseHelper类来包装,操作数据库,将rxjava的订阅过程包装起来,并且引入带参数的查询。
主要解决问题就是免去大量的模版代码,rxjava代码,线程操作,数据库操作,都由
最终预览下,我们想调用一个SearchRecordDao来查询,获取到一个结果list,显示到一个textview上,最终的全部代码如下:
一行代码完成查询过程,不用关心数据库如何创建,dao如何创建,异步线程操作等等都不需要在开发时候重复去操作了。
三.大致结构与流程:
结构:
流程:
四. 主要代码讲解:
1.Dao类注解的标记:
其中@IsQuery :无参数查询所有数据;@IsQueryWithKey 带一个参数的查询,@IsDeleteAll :删除全部。 以上这些方法的Sql其实都可以copy,只是表名不同。
2.DBHelper 中的工作:
当这个Dao被创建标记完成以后,我们马上就可以通过操作 DBHelper来进行操作这个表的查询,比如要调用通过 id查询结果(getSearchRecordById()方法):传一个key,Dao的class,成功获取结果后的回调函数(lambda表达式)
这个调用对应都就是我们刚才在SearchRecordDao.class里的getResearchRecordById()方法,它是通过注解 @ IsQueryWithKey被识别,当我们调用时候传的“1270030”就是id参数。
首先,通过DBHelper找到dao,然后调用dao的对应带参数查询方法,数据库的关闭也在这里进行。queryListWithKey方法如下:
其中的success 和fail 类型为 (t:List)->(Unit)这个代表一个lambda表达式,
对于lambda不熟悉的同学,我简答讲解下怎么理解这个东西。
当这个表达式success(t)被调用的时候,这个方法的设计者会把他查询到的结果(一个list<T>)通过invoke这个表达式的方式传递给你,以便于你的表达式在编写的时候,就可以针对这个查询结果出来以后,如何进行下一步工作给出方案。其中suceess(t) 就是调用lambda表达式,其实它和调用函数是一样的。如果熟悉c,c++的同学,可以发现,它和函数指针的概念很像。
对于用java的同学,可以这样对照lambda和匿名内部类:
{ list-> int size = list.size() } 可以这样理解 ,假设又一个Success接口包含一个参数为list的方法,
success = new Success(){
@override
public void recall(List<T> list){
int size = list.size();
} }
其实任何一个lambda表达式,都可以用一个接口来代替。而相对于接口,lambda可以说,是去掉了定义接口类,创建匿名对象这一过程,可以用一个式子来完成全部工作。
3.Dao中的代码:
dao中的具体实现方法,都在BaseDao中,由父类实现了 insert,delete ,update等方法。子类中主要就是声明query方法,并用注解中写好Sql语句。
BaseDao:
这个方法虽然是在BaseDao中,但我们但子类Dao中会继承这个方法,当它在运行时候,反射获取的所有method是当前子类的所有方法,这样我们在写BaseDao的时候,就可以实现找到将来任意一个子类中代表了有参查询@IsQueryWithKey的方法,并且调用这个方法,在父类里就预先写好RxJava的订阅。