数据库Realm-和SQLite语句说再见
前言
Realm一词在15年末至16年末,一直有出现在稀土掘金的首页,当时总能看到它,而且各种数据库性能测试的文章也总会看到它的身影,当时印象最深的两点便是No SQLite 和 与其它数据库速度差异异常悬殊的条形图,加之自己也被当时的CursorAdapter给深深'伤害'到了,即时是AsyncQueryHandler也拯救不了,最后项目中,我选择了GreenDao,哈哈,因为它可以自动化生成文件,好吧,开玩笑啦,其实需要根据项目需求来作出合适的选择。不过,今天还是要对Realm做一次小结,毕竟新事物总有取代旧事物的一天,虽然你很难知道是哪一个新事物成功了,但我们要紧跟时代的步伐,嘿嘿。
GitHub链接
Realm数据库使用Demo-RealmDemo
使用流程
为了和上一篇的ROOM数据库的使用作对比,仍然以创建一个数据库,并创建一张animal表和一张food表,因为Realm不是关系型数据库,所以没有外键的说法,如果要添加约束,需要自己手动添加增删查改时的操作规范.
1.开发环境
JAVA1.8
Android Studio 3.0 Canary3
Gradle 4.0-milestone-1
2.project的build.gradle中添加
gradle1app的build.gradle中添加
gradle2Demo中还需要额外的RxJava2.0和RxAndroid2.0依赖库
3.只需要创建实体文件Entity,比ROOM省了Dao文件和Database文件
创建数据库和animal表只需要一个Animal类
etity(1)Animal类
Realm规范
a. @RealmClass + RealmModel 接口
以上两个需要一起使用, 作用和ROOM的@Entity注解一样,声明一个类需要构造数据库表.
b. RealmObject类 或者只继承该类,作用和上面两个是一样的,但是这样就不能继承其它类了,比如DataBinding中的BaseObservable,或者自己定义的基类,所以建议使用第一种方式方式。
@RealmClass+RealmModel 或者 RealmObject 必选其一
@PrimaryKey 主键注解,非必须,声明一个变量为主键,可以不设置主键,也没有提供增长的属性,而ROOM则必须显示声明一个主键.
@Required 声明字段不能为null,如果如果为null而执行数据库写的操作,会报错,不能给基础数据类型添加该注解,因为基础数据类型会默认初始化
@Ignore 忽略注解,注解的字段不会添加到数据库中.
@Index 索引注解,
RealmList<Country> Animal中的集合,数组类型的变量必须使用该类代替.
animal(2)Country类
Animal的引用类型变量Country 也需要实现Realm规范,而在ROOM中,同样的情况,Country不需要添加必须的@Entity和@PrimaryKey注解.
country(3)Food类
Realm没有提供类似关系型数据库的外键约束,所以需要自己手动编写数据库操作规范。
food(4)Pojo类
Realm不支持Pojo类,写入什么样的类,读出来的就需要声明相同的类接收。
可见,Realm创建Entity实体文件非常简单,只需要让类实现Realm规范即可。
4.Realm没有类似ORM数据库中常用的Dao文件个DataBase文件,下面你就会发现,其实Realm已经将这些数据库操作都封装好提供我们使用。
(1)首先,创建数据库,Realm一共提供了3种创建方式,项目中一般使用方式三。
重要的几个方法:
.init(context) 该方法只能调用一次,否则会褒报异常。
.name(xxx.realm),指定生成数据库的名字,支持生成多个数据库。
.migration(Class<Migration>),app版本更新时,数据库迁移和表的字段更新需要该方法
.inMemory() 生成的数据库生命周期和app进程一致,app进程销毁后,数据库也会从内存中删除,不会保存到本地磁盘。
.deleteRealmMigrationNeeded(),a
方式一:
init1方式二:
init2方式三:
init3(2)Realm数据库迁移和更新
update_version5.得到Realm对象后,就可以作增删查改的操作了
(1)插入
无主键类
使用.createObject(Class)
有主键类
使用.createObject(Class<E>,主键)
a. 在主线程中插入
第一:需要在 realm.beginTransaction()和realm.commitTransaction()方法间完成插入操作,
第二:返回的RealmResults<Animal>是AbstractList的实现类,可以直接赋值给适配器.
第三:设置主键后,不能重复插入主键相同的对象.
注意,该方式在执行复杂的操作时,有可能会导致ANR,不过单个的复杂程度不高的数据操作还是很快的,不会感觉到丝毫卡顿,可根据实际情况是否选择异步操作方式。
insert1b. Realm还支持通过Json,JsonObject,JsonArray和InputStream等方式插入
以Json插入
json以JsonArray插入
jsonarray以上两种方式非常方用来做项目中的网络数据的缓存。
c. 在子线程中操作事务
transaction 1同时记得在页面销毁时取消这个 transaction.
transaction 2d. 在子线程中来执行操作
下图示例中,
第一 :realm对象是在外面的主线程创建的,不能在Rxjava中的io线程中使用,需要重新创建。
第二:在Rxjava的io线程查询到的RealmResults<E>,在主线程的onNext()回调时,不能直接使用,比如赋值到适配器中,需要像onComplete()那样,重新查询。
rx1接上图
rx2e.使用异步方式(Async后缀的方法)来进行数据库操作
第一:Realm支持多个线程同时读写操作,非常强大,但强大的前提限制是特点1和2。
第二:使用异步的方式,需要注意获取的RealmResults<E>可能为空,所以需要加入监听来获知是否操作完成。
第三:使用异步的方式法,不再需要begin和commit两个事物方法。
async1 async2 async3(2)删除
使用.where(Class<E>)
条件方法
Realm封装了非常多的Dao方法
下面是简单示例.
delete下面贴上网上大虾总结的方法,用法和SQLite语句的关键词差不多,见名知意。
way(3)查询
使用.where(Class<E>)
根据Animal中的Country对象的cid查询animal,这个用法对比SQLite来说非常简单.
query(4)更新
Realm的改操作很简单,在事务中,找到需要更新的对象,直接更改这个对象的数据后,会同步到对应的数据库中.
upate以上就是Realm的主要使用.
注意事项
上面可以看到,代码的注释中特别标注了不少特点,对,这个很重要,这也是Realm强大和坑所在的地方.
1. 特点1:Realm在哪个线程创建就只能在哪个线程使用,下面这样插入会报异常.
2. 特点2:RealmObject在哪个线程创建就只能在哪个线程使用,下面这样也会报异常.
3. 特点3:Realm支持多线程同时读写同一个数据库,前提是特点1和2
,官方建议Realm事务操作使用Async后缀的方法,但需要注意以下findAllAsync()方法返回的list
,如果立即使用是有可能为null,需要加入监听.
4. 特点4::RelamObject 是不支持序列化的,即时实现了Serializable接口也会报错,所以不能直接用Intent传递,只能传递id之类的特征值,进入到下个界面后,再用Realm查询.
5. 特点5:Realm对象调用close()方法关闭后,其查询出来的RealmObject将不能再被访问,比如将其设置给适配器.
6. 特点6: Relam数据库支持自动更新数据对象RealmObject,比如在FoodActivity中删除animal表的第一行数据后,返回MainActivity,直接调用adapter.notifyDataSetChanged(),可以发现RecyclerView中的视图也已经更新,而之前并没有重新查询animal表后再更新AnimalAdapter的animals的的代码,这个特点在开发中是非常有用的,因为页面交错跳转之后,这些页面间的数据同步是个很头疼的问题(至于为什么会有页面的交错跳转,这个锅还是让产品背吧),这个不明白可以查看Demo中的演示。
如果需要了解更多关于Realm的设计原理,可以参考这个地址,说的非常明细-------Realm中文网。
现在,ROOM(可以查看我关于ROOM的文章)和Realm两者的使用方法和差异都很明了,ROOM稳定,成熟,使用起来更自由,对项目的侵入性比较低,而Realm则反之,但同时具有ROOM之类的ORM数据库不具备的高速,简炼,页面间数据同步,多线程保护等优点,但只有多运用,才能知道它们的差异和特点,才能根据自己的项目来选择合适的数据库,快来试试吧。
如果喜欢我的文章,给个赞或者star吧。