Realm的使用

Tag:对于自己认定的事情,要坚持。

Realm 是一个移动数据库,可运行于手机、平板和可穿戴设备之上。可以让你的应用更快速,带来难以想象的体验。其目标是为了代替 CoreData 和 SQLite 数据库。

易用

Ream 不是在SQLite基础上的ORM,它有自己的数据查询引擎。并且十分容易使用。

跨平台

Realm 支持 iOS & OS X (Objective‑C & Swift) & Android。我们可以在这些平台上共享Realm数据库文件,并且上层逻辑可以不用任何改动的情况下实现移植。

高级

Ream支持加密,格式化查询,易于移植,支持JSON,流式api,数据变更通知等高级特性

Realm的使用条件

  • 目前不支持Android以外的Java
  • Android Studio >= 1.5.1
  • 较新的Android SDK版本
  • JDK version >=7.
  • 支持API 9(Android 2.3)以及之后的版本

一.加入依赖关系

1、在project的build中加入依赖

    buildscript {
        repositories {
             jcenter()
        }
       dependencies {
       classpath "io.realm:realm-gradle-plugin:2.2.1"
       }
 }

2、 在module中加入

    apply plugin: 'realm-android

二.创建和初始化

1、使用RealmConfiguration配置Realm

创建数据库时首先需要调用Realm.init()方法,否则系统报错:Java.lang.IllegalStateException: Call Realm.init(Context) before creating a RealmConfiguration
数据库初始化主要工作是什么数据库的name(设置数据库的文件名称)、schemaVersion(设置当前数据库版本号,当前版本号不能小于历史版本号)、migration(数据库版本迁移)、 deleteRealmIfMigrationNeeded(版本冲突时删除原始数据库)、inMemory(在内存中持久化,关闭后自动删除)、build(创建数据库)。

public static Realm getRealm(Context context)
{
byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
Realm.init(context);
Migration migration = new Migration();
RealmConfiguration config = new RealmConfiguration.Builder()
        .name("realmdb.realm") //文件名
        .schemaVersion(0) //版本号
        .migration(migration)//数据库版本迁移(数据库升级,当数据库中某个表添加字段或者删除字段)
        .deleteRealmIfMigrationNeeded()//声明版本冲突时自动删除原数据库(当调用了该方法时,上面的方法将失效)。
        .build();//创建
return Realm.getInstance(config);
}

2、 创建model

创建一个类名为User的model,首先该类必须继承自RealmObject,只有这才用使用Realm来操作model生成相应的表。
在Realm支持的类型有:boolean, byte, short,int,long,float, double,String, Date,byte[], RealmObject, RealmList<? extends RealmObject>还支持Boolean, Byte, Short, Integer, Long, Float 和 Double。但是:整数类型 short、int 和 long 都被映射到 Realm内的相同类型(实际上为 long )。

@PrimaryKey——表示该字段是主键

PrimaryKey就是主键。使用@PrimaryKey来标注,字段类型必须是字符串(String)或整数(byte,short,int或long)以及它们的包装类型(Byte,Short, Integer, 或 Long)。一个model中不可以存在多个主键,使用字符串字段作为主键意味着字段被索引(注释@PrimaryKey隐式地设置注释@Index)。

@Required——表示该字段非空

在某些情况下,某些属性是不能为null的。使用@Required可用于用于强行要求其属性不能为空,只能用于Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[] 和 Date。在其它类型属性上使用

@Required修饰会导致编译失败。

note:基本数据类型不需要使用注解 @Required,因为他们本身就不可为空。

@Ignore——表示忽略该字段

如果想忽略某个字段,则可以使用@Ignore修饰该字段,被秀@ignore修饰的字段在存储数据时会忽略该字段。

@Index——添加搜索索引

使用@index可以为字段添加搜索索引,使用索引以后使得插入的速度变慢,数据量也变得更大。但是在查询速度将变得更快,所以建议只在优化读取性能的特定情况时添加索引。

支持索引:String,byte,short,int,long,boolean和Date字段。

public class User extends RealmObject
{
@PrimaryKey
private int id;
private int age;
@Required
private String name;
public int getId()
{
    return id;
}
public void setId(int id)
{
    this.id = id;
}
public String getName()
{
    return name;
}
public void setName(String name)
{
    this.name = name;
}
private RealmList<Dog> dogs;
public RealmList<Dog> getDogs()
{
    return dogs;
}
public void setDogs(RealmList<Dog> dogs)
{
    this.dogs = dogs;
}
public int getAge()
{
    return age;
}
public void setAge(int age)
{
    this.age = age;
}
}

4、增

写入操作需要在事务中进行,
*使用executeTransaction方法来开启事务。
当model没有primaryKey时,可以使用下面的方法

mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
    User user = realm.createObject(User.class);
    user.setName("Gavin");
    user.setAge(23);
    user.setId(1);
}});      

当model有primaryKey时,则可以使用下面的方法,将对@primaryKey修饰的字段直接在创建对象是赋值(createObject(User.class,1))

mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
    User user = realm.createObject(User.class,1);
    user.setName("Gavin");
    user.setAge(23);       
   }});

使用copyToRealmOrUpdate或copyToRealm方法插入数据

当model中含有primaryKey时使用copyToRealmOrUpdate插入数据,在当前插入主键的值存在时则修改对应主键值相关的数据信息,否则插入数据。
当model中不含有主键时,则使用copyToRealm。若在不含有主键的model中使用copyToRealmOrUpdate将抛异常:

java.lang.IllegalArgumentException: A RealmObject with no @PrimaryKey cannot be updated:XXXXX
   final User user = new User();
user.setName("李德华");
user.setUserId("admin");
user.setAge(25);
user.setId(1);

realm.executeTransaction(new Realm.Transaction()
{
    @Override
    public void execute(Realm realm)
    {                
        realm.copyToRealmOrUpdate(user);
    }
});

当在model(User)中包办另外一个model例如在User中包含一个Dog模型,也可以包含一个Dog的集合
还记得在之前的User数据模型中含有这么一块代码吗?这个申明表示一个User中可以包含(养)多条狗(Dog)
在Realm中List类型表示为RealmList。

private RealmList<Dog> dogs;
public RealmList<Dog> getDogs()
{
    return dogs;
}
public void setDogs(RealmList<Dog> dogs)
{
    this.dogs = dogs;
}      

向数据模型(User)插入另外一个数据模型(Dog)时,可以才用如下方式(当User含有主键时采用):

private void insertOrUpdateDataWithPrimaryKey()
{
final User user = new User();
user.setName("李德华");
user.setUserId("admin");
user.setAge(25);
user.setId(1);
realm.executeTransaction(new Realm.Transaction()
{
    @Override
    public void execute(Realm realm)
    {
        Dog dog1 = new Dog();
        dog1.setId(1);
        dog1.setAge(5);
        dog1.setName("藏獒");
        dogs = new RealmList<Dog>();
        dogs.add(dog1);
        user.setDogs(dogs);
        realm.copyToRealmOrUpdate(user);
    }
});
}    

当user没有主键时使用如下方式:

realm.executeTransaction(new Realm.Transaction()
{
@Override    
public void execute(Realm realm) {
    User user = realm.createObject(User.class);        
    user.setName("Gain");        
    user.setAge(23);       
    Dog dog1 = realm.createObject(Dog.class);        
    dog1.setAge(1);        
    dog1.setName("二哈");        
    user.getDogs().add(dog1);        
    Dog dog2 = realm.createObject(Dog.class);        
    dog2.setAge(2);        
    dog2.setName("阿拉撕家");        
    user.getDogs().add(dog2);    
}
});

使用异步方式进行数据插入,具体的方式如下:

使用异步插入,可以检测数据是否插入成功,成功后可以使用new Realm.Transaction.OnSuccess()进行监测,失败可以使用new Realm.Transaction.OnError()监测。

/**
 * 异步执行
 * 向有主键的数据表中insert或update数据
 * 数据表中有当前主键时则执行update
 * 否者执行insert
 */
private void insertOrUpdateDataWithPrimaryKeyAsync()
{
final User user = new User();
user.setName("李德华");
user.setUserId("admin");
user.setAge(25);
user.setId(1);

task = realm.executeTransactionAsync(new Realm.Transaction()
{
    @Override
    public void execute(Realm realm)
    {
        Dog dog1 = new Dog();
        dog1.setId(1);
        dog1.setAge(5);
        dog1.setName("藏獒");
        dogs = new RealmList<Dog>();
        dogs.add(dog1);
        user.setDogs(dogs);
        realm.copyToRealmOrUpdate(user);
    }
}, new Realm.Transaction.OnSuccess()
{
    @Override
    public void onSuccess()
    {
        Logger.d("Success");
    }
}, new Realm.Transaction.OnError()
{
    @Override
    public void onError(Throwable error)
    {
        Logger.d("Error");
    }
});
}

注意:如果当Acitivity或Fragment被销毁时,在OnSuccess或OnError中执行UI操作,将导致程序奔溃 。用RealmAsyncTask .cancel();可以取消事务,在onStop中调用,避免crash

@Override
protected void onStop()
{
super.onStop();
if (task != null && task.isCancelled())
{
    task.cancel();
}
}   

将Json数据直接插入数据库

Realm有一个强大的功能就是将Json数据直接插入到相应的数据库,具体的代码入下:

/**
* 将Json数据添加到数据表中
*/
private void insertDataFromJson()
{
realm.executeTransaction(new Realm.Transaction()
{
    @Override
    public void execute(Realm realm)
    {
        realm.createOrUpdateObjectFromJson(User.class, "{ id: 2, age: 50, name:\"sealong\",userId:\"admin\",
                      dogs:[{id:5,name:\"龙骑士\",age:3},{id:6,name:\"猪骑士\",age:4},{id:7,name:\"马骑士\",age:5}]}");
    }
});
}

Realm 解析 JSON 时遵循如下规则:
1.使用包含空值(null)的 JSON 创建对象:

2.对于非必须(可为空值的属性),设置其值为 null;

3.对于必须(不可为空值的属性),抛出异常;

4.使用包含空值(null)的 JSON 更新对象:

5.对于非必须(可为空值的属性),设置其值为 null;

6.对于必须(不可为空值的属性),抛出异常;

7.使用不包含对应属性的 JSON: * 该属性保持不变

5.删

Realm的删除,包含两个步骤,首先查找到相应的数据集合,在在集合中删除相应的数据,代码如下:

private void deleteData()
{    //先查找到数据    
 final RealmResults<User> userList = realm.where(User.class).findAll();    
 realm.executeTransaction(new Realm.Transaction()    
 {        
     @Override        
     public void execute(Realm realm)        
     {            
         userList.get(4).deleteFromRealm();//删除指定位置(第4条记录)的记录
         userList.deleteFromRealm(4);//删除指定位置(第4条记录)的记录            
         userList.deleteFirstFromRealm(); //删除user表的第一条数据            
         userList.deleteLastFromRealm();//删除user表的最后一条数据            
         userList.deleteAllFromRealm();//删除user表的全部数据        
     }    
  });
}

6.改

改是通过先查找到相应的数据,然后再设置需要修改的相应的信息。
例如修改User表中name=sealong 并且Id=2的所有对象,并将其UserId设置为user的代码如下。

private void dataUpdate()
{
realm.executeTransaction(new Realm.Transaction()
{
    @Override
    public void execute(Realm realms)
    {
        //先查找后得到User对象
        RealmResults<User> users = realm.where(User.class).equalTo("name", "sealong").equalTo("id", 2).findAll();
        for (User user : users)
        {
            user.setUserId("user");
        }
    }
});
}

7.查

查找常用的函数有:
findAll ---- 查询满足条件的所有记录

findAllAsync----异步查询(当数据量较大,可能会引起ANR的时候,就可以使用findAllAsync)

findFirst ---- 查询第一条数据

equalTo ---- 根据条件查询(多次使用该函数可以实现多条件查询)

sum ---- 对指定字段求和。

average ---- 对指定字段求平均值。

min ---- 对指定字段求最小值。

max ---- 对指定字段求最大值。count : 求结果集的记录数量。

findAll ---- 返回结果集所有字段,返回值为
RealmResults队列

findAllSorted ---- 排序返回结果集所有字段,返回值为RealmResults队列

between ---- 指定字段的值在某区间内

greaterThan()|lessThan()----指定字段的值大于(小于)给定的值

greaterThanOrEqualTo() & lessThanOrEqualTo()----指定字段的值大于等于(小于等于)给定的值

equalTo() & notEqualTo() ---- 指定字段的值(不)等于

contains() ----指定字段的值包含某些字符(指定字段为String)

beginsWith() & endsWith() ----指定字段的值以给定的值开始(结束)

isNull() & isNotNull() ----指定字段的值(不)为null

isEmpty() & isNotEmpty() ----指定字段的值(不)为空

private void dataSelectUser()
{
    RealmResults<User> users = realm.where(User.class).findAllSorted("id", Sort.DESCENDING);//按照id倒序排序
long size = users.size();//返回数据的条数
double average = users.average("age");//返回查询结果的中age的平均值
long sum = users.sum("age").longValue();//返回查询结果中age的总数
long min = users.min("age").longValue();//返回查询结果中age的最小值
long max = users.max("age").longValue();//返回查询结果中age的最大值
}

8.数据库升级

当某个数据表需要加某些字段或者添加某些属性时,而且不删除之前的原始数据的情况下,需要对原始数据进行升级
例如需要对表删除字段age,并且需要添加一个String类型的userId 并且设置将之前表中的userId的值设置为1,同时版本号需要更新

public class Migration implements RealmMigration
{
/**
 * 升级数据库
 */
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion)
{
    RealmSchema schema = realm.getSchema();
    if (oldVersion != newVersion)
    {
        RealmObjectSchema personSchema = schema.get("User");
        //新增@Required的id
        personSchema.addField("userId", String.class, FieldAttribute.REQUIRED)
                .transform(new RealmObjectSchema.Function()
                {
                    @Override
                    public void apply(DynamicRealmObject obj)
                    {
                        obj.set("userId", "1");//为id设置值
                    }
                }).removeField("age");
        oldVersion++;
    }
}
}

转载自: Realm在android的应用

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

推荐阅读更多精彩内容