数据库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中添加

数据库Realm-和SQLite语句说再见_第1张图片
gradle1

   app的build.gradle中添加

数据库Realm-和SQLite语句说再见_第2张图片
gradle2

Demo中还需要额外的RxJava2.0和RxAndroid2.0依赖库

3.只需要创建实体文件Entity,比ROOM省了Dao文件和Database文件

创建数据库和animal表只需要一个Animal类

数据库Realm-和SQLite语句说再见_第3张图片
etity

(1)Animal类

Realm规范

a.   @RealmClass  +  RealmModel  接口      

以上两个需要一起使用, 作用和ROOM的@Entity注解一样,声明一个类需要构造数据库表.

b.  RealmObject类        或者只继承该类,作用和上面两个是一样的,但是这样就不能继承其它类了,比如DataBinding中的BaseObservable,或者自己定义的基类,所以建议使用第一种方式方式。

@RealmClass+RealmModel  或者 RealmObject        必选其一

@PrimaryKey        主键注解,非必须,声明一个变量为主键,可以不设置主键,也没有提供增长的属性,而ROOM则必须显示声明一个主键.

@Required            声明字段不能为null,如果如果为null而执行数据库写的操作,会报错不能给基础数据类型添加该注解,因为基础数据类型会默认初始化

@Ignore                 忽略注解,注解的字段不会添加到数据库中.

@Index                   索引注解,

RealmList   Animal中的集合,数组类型的变量必须使用该类代替.

数据库Realm-和SQLite语句说再见_第4张图片
animal

(2)Country类

Animal的引用类型变量Country 也需要实现Realm规范,而在ROOM中,同样的情况,Country不需要添加必须的@Entity和@PrimaryKey注解.

数据库Realm-和SQLite语句说再见_第5张图片
country

(3)Food类

Realm没有提供类似关系型数据库的外键约束,所以需要自己手动编写数据库操作规范。

数据库Realm-和SQLite语句说再见_第6张图片
food

(4)Pojo类

Realm不支持Pojo类,写入什么样的类,读出来的就需要声明相同的类接收。

可见,Realm创建Entity实体文件非常简单,只需要让类实现Realm规范即可。

4.Realm没有类似ORM数据库中常用的Dao文件个DataBase文件,下面你就会发现,其实Realm已经将这些数据库操作都封装好提供我们使用。

(1)首先,创建数据库,Realm一共提供了3种创建方式,项目中一般使用方式三。

重要的几个方法:

.init(context)            该方法只能调用一次,否则会褒报异常。

.name(xxx.realm),指定生成数据库的名字,支持生成多个数据库。

.migration(Class),app版本更新时,数据库迁移和表的字段更新需要该方法

.inMemory()             生成的数据库生命周期和app进程一致,app进程销毁后,数据库也会从内存中删除,不会保存到本地磁盘。

.deleteRealmMigrationNeeded(),a

方式一:

数据库Realm-和SQLite语句说再见_第7张图片
init1

方式二:

数据库Realm-和SQLite语句说再见_第8张图片
init2

方式三:

数据库Realm-和SQLite语句说再见_第9张图片
init3

(2)Realm数据库迁移和更新

数据库Realm-和SQLite语句说再见_第10张图片
update_version

5.得到Realm对象后,就可以作增删查改的操作了

(1)插入

无主键类

使用.createObject(Class)

有主键类

使用.createObject(Class,主键)

a. 在主线程中插入

第一:需要在 realm.beginTransaction()和realm.commitTransaction()方法间完成插入操作,

第二:返回的RealmResults是AbstractList的实现类,可以直接赋值给适配器.

第三:设置主键后,不能重复插入主键相同的对象.

注意,该方式在执行复杂的操作时,有可能会导致ANR,不过单个的复杂程度不高的数据操作还是很快的,不会感觉到丝毫卡顿,可根据实际情况是否选择异步操作方式。

数据库Realm-和SQLite语句说再见_第11张图片
insert1

b. Realm还支持通过Json,JsonObject,JsonArray和InputStream等方式插入

以Json插入

数据库Realm-和SQLite语句说再见_第12张图片
json

以JsonArray插入

数据库Realm-和SQLite语句说再见_第13张图片
jsonarray

以上两种方式非常方用来做项目中的网络数据的缓存。

c. 在子线程中操作事务

数据库Realm-和SQLite语句说再见_第14张图片
transaction 1

同时记得在页面销毁时取消这个 transaction.

数据库Realm-和SQLite语句说再见_第15张图片
transaction 2

d. 在子线程中来执行操作

下图示例中,

第一 :realm对象是在外面的主线程创建的,不能在Rxjava中的io线程中使用,需要重新创建。

第二:在Rxjava的io线程查询到的RealmResults,在主线程的onNext()回调时,不能直接使用,比如赋值到适配器中,需要像onComplete()那样,重新查询。

数据库Realm-和SQLite语句说再见_第16张图片
rx1 

接上图

数据库Realm-和SQLite语句说再见_第17张图片
rx2

e.使用异步方式(Async后缀的方法)来进行数据库操作

第一:Realm支持多个线程同时读写操作,非常强大,但强大的前提限制是特点1和2。

第二:使用异步的方式,需要注意获取的RealmResults可能为空,所以需要加入监听来获知是否操作完成。

第三:使用异步的方式法,不再需要begin和commit两个事物方法。

数据库Realm-和SQLite语句说再见_第18张图片
async1


数据库Realm-和SQLite语句说再见_第19张图片
async2


async3

(2)删除

使用.where(Class)

条件方法

Realm封装了非常多的Dao方法

下面是简单示例.

数据库Realm-和SQLite语句说再见_第20张图片
 delete

下面贴上网上大虾总结的方法,用法和SQLite语句的关键词差不多,见名知意。

数据库Realm-和SQLite语句说再见_第21张图片
 way

(3)查询

使用.where(Class)

根据Animal中的Country对象的cid查询animal,这个用法对比SQLite来说非常简单.

数据库Realm-和SQLite语句说再见_第22张图片
query 

(4)更新

Realm的改操作很简单,在事务中,找到需要更新的对象,直接更改这个对象的数据后,会同步到对应的数据库中.

数据库Realm-和SQLite语句说再见_第23张图片
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吧。


数据库Realm-和SQLite语句说再见_第24张图片

你可能感兴趣的:(数据库Realm-和SQLite语句说再见)