Realm学习总结

一、入门篇

1.如何安装Realm

第一步:在Project级别的 build.gradle 文件中添加依赖:

buildscript {
    ...
    dependencies {
        ...
        classpath "io.realm:realm-gradle-plugin:5.13.0"
    }
    ...
}

第二步:在app级别的build.gradle文件中添加下面这一行:

apply plugin: 'realm-android'

第三步: 同步( sync )项目配置,Android Studio运行结束后即可完成安装。

2.如何定义Realm对象

定义Realm对象的方法非常简单,只需定义一个Model,然后继承RealmObject即可。

//定义Dog类,继承RealmObject
public class Dog extends RealmObject {
    private String name;
    private int age;
    
    //注意:不能定义构造函数,否则编译不通过。
    //如若想定义有参构造函数,则必须要有一个public修饰的无参构造函数
    //public Dog(String name, int age) {
    //    this.name = name;
    //    this.age = age;
    //}
    
    //自动生成的标准 getter\setter方法 
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

3.如何初始化Realm数据库

//Realm数据库的默认初始化方式:
Realm.init(context); //初始化数据库
Realm realm = Realm.getDefaultInstance();//获取默认配置的实例

Realm数据库在使用前需要被初始化,因为需要向init(Context context)函数传递context,因此我们可以在Application中进行初始化操作:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Realm.init(this);
    }
}

如果在Application类中初始化,那么我们还需对AndroidManifest.xml做如下修改:

<application
    android:name=".MyApplication"
    ...
</application>

4.如何使用Realm数据库

//向realm数据库中存储Dog对象
realm.beginTransaction();
Dog dog = realm.createObject(Dog.class);
dog.setAge(2);
dog.setName("金毛");
realm.commitTransaction();

5.如何查看Realm数据库

默认Realm数据库文件名为default.realm,我们可以使用官方推荐的Realm Studio软件查看、编辑数据库。

二、基础篇

1.Realm Model

(a)属性类型

上面简单介绍过Realm Model的创建方式,这里不再赘述。Realm Model支持定义多种类型的字段,如:booleanbyteshortintlongdoubleStringDatebyte[]类型。其中,byteshortintlong都将会被应设成long类型。同时,Realm还支持ReamlObject派生的子类和ReamlObject数组。
此外,Realm Model还支持以上几种类型对应的包装类型,并且默认值为null

(b) 非空属性

Realm Model通过注解@required来声明属性值是否非空。这里需要注意的是非空是针对默认值为null的属性而言,也就是说只有上述几种基本类型的包装类型才能被声明为@required

public class Dog extends RealmObject {
    @Required //正确。String类型的属性默认值为null,因此可以声明为required
    private String name;
    @Required //错误。int类型的默认值为0,声明为required毫无意义
    private int age;
    ......
}

(c) 主键

Realm数据库同样能够给数据表设置主键,支持设置主键的类型包括:byteshortintlongByteShortIntegerLong以及String。设置主键的方式是在对应的字段加注解@PrimaryKey,但是不支持联合主键。值得注意的是,如果将String类型的属性设置为了主键,那么将会隐示地为其添加@Index注解。

public class Dog extends RealmObject {

    @PrimaryKey
    private String name;
    
    private int age;
    
    ...
}

关于设置主键有以下几点需要注意:

  1. 如果使用copyToRealmOrUpdateinsertOrUpdate方法来创建RealmObject对象的话,则必须为其设置主键,否则将会跑出异常;
  2. 如果设置了主键,那么查询速度将会稍微快一点,但是插入和更新操作将会稍微变慢,性能的变化取决于数据的规模;
  3. 由于Realm.createObject方法返回的是一个所有属性被赋予默认值的对象,因此如果有具备有主键的RealmObject通过这种方式创建后未被赋值,那么再次调用这个方法来创建具备主键的RealmObject将会产生冲突。解决这个问题的方式是先创建一个RealmObject对象,然后给其字段赋值,再调用copyToRealminsert方法加入数据库;
  4. 被设置为主键的String类型或包装类型字段可以被设置为null,除非同时设置@PrimatyKey@Required注解。

2.使用 Realm Object

(a) 对象自动更新

RealmObject是实时、自动将视图更新到基础数据中的,并且对象的更新能够立即作用到(已有的)查询结果中。

Realm realm = Realm.getDefaultInstance();

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Dog dog1 = realm.createObject(Dog.class);
        dog1.setName("牧羊犬");
        dog1.setAge(2); // age = 2
    }
});

Dog dog2 = realm.where(Dog.class).equalTo("age",2).findFirst();
Log.i("Age","dog age is:"+dog2.getAge()); // 输出结果: 2

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Dog dog3 = realm.where(Dog.class).equalTo("age",2).findFirst();
        dog3.setAge(4); // age = 4
    }
});

Log.i("Age","Now dog age is:"+dog2.getAge()); // 输出结果: 4(已经不是2了哦)

//以上代码段经运行后结果:
08-15 15:57:21.460 21792-21792/com.chivan.realmdemo I/Age: dog age is:2
08-15 15:57:21.460 21792-21792/com.chivan.realmdemo I/Age: Now dog age is:4

这中特性不仅能够使得Realm快速、高效,也能使你的代码变得更加简洁和更具有交互性。更重要的是,如果你的ActivityFragment依赖于某个特定的RealmObject或者RealmResults实例,那么更新UI之前无需担心数据刷新或者重新拉取数据。

(b)自定义RealmObject对象

RealmObjectJavaBean类似,我们也可以直接继承RealmObjectRealmObject的属性权限可以是publicprotected或者private。如果声明为了public权限,那么无需定义gettersetter方法,可以直接给属性赋值。

// 定义 Cat 类,继承自 RealmObject
public class Cat extends RealmObject {
    public String name;
    public String age;

//    注意:name、age均是public权限,因此无需getter、setter方法
//    public String getName() {
//        return name;
//    }
//
//    public void setName(String name) {
//        this.name = name;
//    }
//
//    public String getAge() {
//        return age;
//    }
//
//    public void setAge(String age) {
//        this.age = age;
//    }
}

// 创建 cat 对象,并给对象属性赋值
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Cat cat = realm.createObject(Cat.class);
        cat.name = "波斯猫";
        cat.age = 2;
    }
});

Cat类中我们定义了nameage属性,除此之外还可以添加其他自定义函数。

(c)RealmModel接口

除了继承RealmObject外,还可以实现RealmModel接口,并配合@RealmClass注解。这种情况下,所有RealmObject对象的属性只能通过静态方法来调用。

// 继承 RealmObject
cat.deleteFromRealm();
cat.isValid();

//实现 RealmModel 接口
RealmObject.deleteFromRealm(cat);
RealmObject.isValid(cat);

3.插入操作

与读操作不同的是,Realm的写操作必须包含在事务(transaction)中。在事务的结尾,我们可以选择提交事务或者取消事务。事务一旦被提交,那么事务作出的所有改变都将会被写入磁盘。事务被取消后,那么所有的改变都将失效。换句话说,同一个事务中的操作要么全都做、要么就全都不错,这保证了数据的一致性和线程安全。

realm.beginTransaction();//开始事务
// do some things...
realm.commitTransaction();//提交事务

realm.beginTransaction();//开始事务
// do some things...
realm.cancelTransaction();//取消事务

需要注意的是Realm的写操作会互相死锁,因此如果你在UI线程和后台线程中同时开启了写入事务,那么将可能会导致ANR错误。为了避免这种事件发生,官方建议 在异步事务(async transaction)处理写入事务。

如果在事务中发生了异常,那么事务中所有的改变都将消失,但是Realm本身不受影响。如果捕捉了事务中的异常,并且程序将会继续执行下去,那么需要取消产生异常的事务。有个省事的做法就是使用excuteTransaction来执行事务,那么产生异常后的后续操作都将会自动执行,无需人工参与。

得益于RealmMVCC架构,即使开启了事务,我们仍然能够同时开启读操作。当你提交了一个写事务后,所有其他Realm实例都会收到通知并自动更新。

(a)创建对象

在事务中用createObject方法创建对象:

realm.beginTransaction(); //开启事务
Dog dog = realm.createObject(Dog.class); //创建 Dog 对象
dog.setAge(2);
dog.setName("金毛");
realm.commitTransaction(); //提交事务

如果是首先创建了一个对象,然后使用copyToRealm将对象写入到Realm数据库,那么你需要将这个操作放入事务中提交。Realm支持无限个自定义有参构造函数来创建对象,但前提是必须至少要有一个public修饰的无参构造函数。否则将会报错:Class "Cat" must declare a public constructor with no arguments if it contains custom constructors.


Cat cat = new Cat("小奶猫"); // 有参构造函数
cat.setAge(5);
realm.beginTransaction();
Cat catRealm = realm.copyToRealm(cat); //将对象拷贝到 Realm 数据库中
realm.commitTransaction();

这里需要强调的是只有在返回的对象(上述代码中的catRealm对象)上的操作会生效,而原始的对象(上述代码中的cat对象)上的操作将不会同步到Realm数据库中。如果你仅仅是向Realm数据库中插入一个对象,而不会立马对对象做出修改的话,那就尽可能用insert方法。insert方法的实现方式和copyToRealm方法很相似,但是前者速度更快,因为它不用返回被插入对象的副本。如果想要插入多个对象,推荐使用insertinsertOrUpdate方法(所有的插入操作都需要放到事务块中)。

List<Cat> cats = Arrays.asList(new Cat("金毛"), new Cat("小奶猫"));
realm.beginTransaction();
realm.insert(cats); //插入多个Cat对象
realm.commitTransaction();

(b)异步事务

前文讲到如果在UI线程和后台线程中同时开启了写入事务,那么可能会导致ANR。因此,为了避免阻塞UI线程,我们可以采用异步事务来避免这个问题。

Realm realm = Realm.getDefaultInstance();
realm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Cat cat = realm.createObject(Cat.class);
        cat.setName("小黄猫");
        cat.setAge(3);
    }
}, new Realm.Transaction.OnSuccess() {
    @Override
    public void onSuccess() {
        // 写入成功
    }
}, new Realm.Transaction.OnError() {
    @Override
    public void onError(Throwable error) {
        // 写入失败
    }
});

以上代码中的onSuccessonError回调参数是可选的,如果传入了相应的回调函数,那么出发之后将会执行相应回调函数。需要注意的是回调函数在Lopper线程中执行。

(c)对象批量更新

如果对对象某个字段进行批量更新,最高效的方式是先查询到所有符合条件的对象,然后调用RealmResults.setXXX()方法。

//批量更新对象属性值
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        RealmResults<Cat> cats = realm.where(Cat.class).equalTo("name","小奶猫").findAll();//查询所有name = "小奶猫"的对象 
        cats.setValue("age",3); //将符合条件到的所有Cat对象age属性值设置为3
    }
});

其中setXXX()函数的第一个参数是对象字段名,第二个参数是字段值。

4.查询操作

Realm的查询引擎使用Fluent接口来构造多子句查询。所有提取(包括查询)在Realm中都是懒加载模式,并且永远不会复制数据。例如查询所有name="小奶猫"并且age = 2的对象,我们可以按如下方式来查询:

RealmResults<Cat> cats2 = realm.where(Cat.class)
        .equalTo("name","小奶猫")
        .and()
        .equalTo("age",2)
        .findAll();

(a)结果过滤

where方法通过指定被查询的model来开始RealmQuery。过滤条件使用谓词作为名称的函数指定,其中大多数函数的名称不言自明(例如,equalTo),过滤函数将字段名称作为其第一个参数。

使用in函数从查询结果集中匹配相应的字段,例如要查找name小奶猫金毛或者小花猫的对象,我们可以使用以下语法:

RealmResults<Cat> cats3 = realm.where(Cat.class)
        .in("name", new String[]{"小奶猫","小花猫","金毛"})
        .findAll();

in过滤函数支持字符串二进制数据数值类型日期类型。其中数值类型日期类型还额外支持以下几种过滤操作:

  • between
  • graterThan
  • lessThan
  • greaterThanOrEqualTo
  • lessThanOrEqualTo

字符串类型额外支持以下几种过滤操作:

  • contains
  • beginsWith
  • endsWith
  • like

其中,字符串类型有额外的第三个参数来控制是否大小写敏感:Case.INSENSITIVECase.SENSITIVE,默认是Case.SENSITIVE

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容