【Android】Realm代替SQLite详解

目录

 

一、简介

二、环境配置

先决条件:

安装配置:

三、使用

3.1.初始化Realm

3.2创建实体

3.3 事务操作

3.4 增

3.5 查

3.6 改

3.7 删

3.8 版本升级(数据迁移)

3.9 加密

四:问题

主键(包括自增长)

不是自增长

自增长

支持SQL

Intent传递:

五:配合使用

rxjava

多线程例子

加密

单元测试

多进程

适配器

JSON

六 :Demo


一、简介

Realm是一个嵌入式数据库,是一个MVCC(多版本并发控制)数据库。他比SQLite轻量级,执行效率更高,使用更加方便

。它实现了零拷贝、内部加密等,并且支持JSON、流式api、Rxjava等。当然也兼容ACID,并且跨平台。

二、环境配置

先决条件:

  • Android Studio 1.5.1或更高版本
  • JDK 7.0或更高版本
  • 最新版本的Android SDK
  • Android API等级9或更高(Android 2.3及更高版本)

注意: Realm不支持Android之外的Java。我们不再支持Eclipse作为IDE; 请迁移到Android Studio。

安装配置:

1.在项目的build文件加上

 dependencies {

        classpath 'com.android.tools.build:gradle:3.0.1'


        classpath "io.realm:realm-gradle-plugin:5.7.0"

        // NOTE: Do not place your application dependencies here; they belong

        // in the individual module build.gradle files

    }

2.在app的build文件加上


apply plugin: 'realm-android'

三、使用

3.1.初始化Realm

1 在Application的onCreate()方法中调用Realm.init()


public class MyApplication extends Application {

  @Override

  public void onCreate() {

    super.onCreate();

    Realm.init(this);

  }

}

2. 在Application的oncreate()方法中对Realm进行相关配置

  • 使用默认配置

public class MyApplication extends Application{


    @Override

    public void onCreate() {

        super.onCreate();

        Realm.init(this);

        //默认配置会默认创建一个default.realm的Realm的文件,位于Context.getFilesDir

        //使用Realm.getPath获取Realm的绝对路径。

        RealmConfiguration config = new RealmConfiguration.Builder().build();

        Realm.setDefaultConfiguration(config);


    }

}
  • 使用自定义配置
 @Override

    public void onCreate() {

        super.onCreate();

        Realm.init(this);

        RealmConfiguration config = new RealmConfiguration.Builder()

                .name("myrealm.realm") //文件名

                .schemaVersion(1) //版本

                .build();

       Realm.setDefaultConfiguration(config);


       //其他会用到的参数

       //encryptionKey() 指定数据库的密钥

       //migration(new StudentInfoBean()) 指定迁移操作的迁移类

      //inMemory()  声明数据库只在内存中持久化


    }

3.2创建实体

1.新建一个实体类继承RealmObject

注意:Realm 不支持嵌套类

public class StudentInfoBean extends RealmObject {

    @Required

    public String name;

    public int age;

    public String sex;

    private RealmList<Course> mCourses;



    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;

    }


    public String getSex() {

        return sex;

    }


    public void setSex(String sex) {

        this.sex = sex;

    }


    public class Course extends RealmObject{


        private String name;


    }

}

也可以实现RealmModel,并且加上注解@RealmClass

@RealmClass

public class StudentInfoBean implements RealmModel {

   

}

2.支持类型:booleanbyteshort,int,long,floatdouble,StringDate和,byte[]RealmObjectRealmList<? extends RealmObject>
还支持BooleanByteShortIntegerLongFloat和 Double
Tip:整数类型 shortint和 long都被映射到 Realm 内的相同类型(实际上为 long )

3.注解字段

  @PrimaryKey

  • 主键(不可以存在多个主键)
  • 字段必须是String、 integer、byte、short、 int、long 以及它们的封装类Byte, Short, Integer, and Long
  • 字符串上的注释会隐式设置注释@Index(即索引)

 

  @Required

  • 表示该字段非空
  • 只有BooleanByteShortIntegerLongFloatDoubleStringbyte[]并且Date可以进行注释@Required。如果将其添加到其他字段类型,编译将失败。

  @Ignore

  • 如果您不想将模型中的字段保存到其Realm,请使用注释@Ignore

  @Index

  • 添加搜索索引
  • 主键一样,这会使写入速度稍慢,但会使读取速度更快。(它还会使您的Realm文件稍大,以存储索引。)最好只在优化特定情况下的读取性能时添加索引。
  • 索引类型StringbyteshortintlongbooleanDate

3.3 事务操作

与读取操作不同,Realm中的写入操作必须包含在事务中

同步:

  • 第一种方式:
realm.beginTransaction(); //开启事务

//do something...

realm.commitTransaction();//提交事务

在异常处理里写上 

realm.cancelTransaction(); //取消事务
  • 第二种方式:

realm提供了事务块,不用手动的写beginTransactioncommitTransaction以及cancelTransaction,你可以使用executeTransaction方法,它会自动处理开始/提交

realm.executeTransaction(new Realm.Transaction() {

    @Override

    public void execute(Realm realm) {

        //do something...

    }

});

异步:

如果大量的操作,那当然需要异步,realm也提供了方法那就是 executeTransactionAsync,同时也提供了成功和失败的回调

注意点:需要在 Activity 或 Fragment 退出的时候需要取消事务。不然退出后更新ui会造成程序崩溃

RealmAsyncTask transaction =realm.executeTransactionAsync(new Realm.Transaction() {

            @Override

            public void execute(Realm bgRealm) {

                //do something

            }

        }, new Realm.Transaction.OnSuccess() {

            @Override

            public void onSuccess() {

                // Transaction was a success.

            }

        }, new Realm.Transaction.OnError() {

            @Override

            public void onError(Throwable error) {

                // Transaction failed and was automatically canceled.

            }

        });


//取消事务

public void onStop () {

    if (transaction != null && !transaction.isCancelled()) {

        transaction.cancel();

    }

}

3.4 增

以下例子全部用同步事务块的例子来操作(异步的方法上面已经讲过了)

注意点:如果有内部类的情况下,只需要也将内部类添加近Realm,再进行外部添加就好了

如下

mRealm.executeTransaction(new Realm.Transaction() {

            @Override

            public void execute(Realm realm) {

                User user = realm.createObject(User.class);

                user.setName("John");

                user.setEmail("john@corporation.com");

                Course course=realm.createObject(Course.class);

                course.setName("English");

                user.setCourse(course);

            }

        });

接下来的例子都是没有内部类的情况

  • 第一种方式:

通过 Realm.createObject() 返回对象并添加到Realm


realm.executeTransaction(new Realm.Transaction() {

    @Override

    public void execute(Realm realm) {

        User user = realm.createObject(User.class);

        user.setName("John");

        user.setEmail("john@corporation.com");

    }

});

 

  • 第二种方式:

通过 Realm.copyToRealm拷贝一个对象并添加到Realm。

通过Realm.copyToRealmOrUpdate 拷贝对象并添加(或更新)到Realm。(什么情况下是更新呢,就是如果包含主键,且主键相同的情况下会更新原数据)

注意点:Realm只管理返回的对象,什么意思呢? Realm.copyToRealm 和 Realm.copyToRealmOrUpdate 会返回一个对象,如果要更改数据,请更改返回对象的数据,而不是最初的源数据

      final User user = new User();

        user.setName("John");

        user.setEmail("john@corporation.com");

        mRealm.executeTransaction(new Realm.Transaction() {

            @Override

            public void execute(Realm realm) {

                realm.copyToRealm(user);//添加

                realm.copyToRealmOrUpdate(user);//添加或更新

            }

        });
  • 第三种方式:

通过 insert 或 insertOrUpdate 直接添加数据,它的好处少一步copy对象。所以如果要插入许多对象,建议使用

insert 或 insertOrUpdate。

注意点:insertOrUpdate 的用法与 copyToRealmOrUpdate 一样

  final User user = new User();

        user.setName("John");

        user.setEmail("john@corporation.com");

        mRealm.executeTransaction(new Realm.Transaction() {

            @Override

            public void execute(Realm realm) {

                realm.insert(user);

                realm.insertOrUpdate(user);

            }

        });

3.5 查

  • 同步查询全部数据

需要用RealmResults接受数据

RealmResults<StudentInfoBean> realmResults=mRealm.where(User.class).findAll()

RealmResults是什么呢?

通过下面的源码追踪 发下最后也是继承 AbstractList 的

注意:虽然也实现了List接口,但是它还是有许多方法不能使用的,比如 add、addAll、remove、clear,都是不能使用的

public class RealmResults<E> extends OrderedRealmCollectionImpl<E>{}


abstract class OrderedRealmCollectionImpl<E>

        extends AbstractList<E> implements OrderedRealmCollection<E> {}


public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {}

            
  • 异步查询全部数据

当你数据量大的时候采用异步查询(当然我都是异步查询的)

注意点:

  1. 异步查询的数据不是马上有的,当然Realm也提供了监听器,注册 addChangeListener 监听器,在回调里进行数据的处理。还有一个方法 realmResults.isLoaded ,这个只能判断是否查询完毕。
  2. 你只能在Looper线程上使用异步查询,如果没有Looper的线程使用异步查询,那么会抛出IllegalStateException
 final RealmResults<User> realmResults = mRealm.where(User.class).findAllAsync();

        realmResults.addChangeListener(new RealmChangeListener<RealmResults<User>>() {

            @Override

            public void onChange(RealmResults<User> user) {

                mMainAdapter.setNewData(user);

            }

        });

  • equalsTo

1.查询字段名是name,内容是John的数据。equalsTo的第一个参数一定是字符串,第二个参数就是你存储的类型

final RealmResults<User> realmResults = mRealm.where(User.class).equalTo("name", "John").findAllAsync();

2.(同时满足多个条件的多条件查询)如果是涉及到类与其内部类的查询 举个例子 如name为John 课程为英语课程的就如下

 RealmResults<User> realmResults = mRealm.where(User.class).equalTo("name", "John").equalTo("course.name", "English").findAllAsync();

3.(满足其中一个条件即可的多条件查询)如果多条件查询  举个例子 查找name为John或者GG的数据

final RealmResults<User> realmResults = mRealm.where(User.class).equalTo("name", "John").or.equalTo("name", "GG").findAllAsync();
  • in

举个例子 查找name为John或者GG的数据,等同于上买的

final RealmResults<User> realmResults = mRealm.where(User.class).equalTo("name", "John").or.equalTo("name", "GG").findAllAsync();
  • findFirst

查询第一条数据

mRealm.where(User.class).findFirst();
  • 聚合

RealmResults 为结果集中的求和和平均值等操作提供聚合便捷方法。


RealmResults<User> results = realm.where(User.class).findAll();

long   sum     = results.sum("age").longValue();

long   min     = results.min("age").longValue();

long   max     = results.max("age").longValue();

double average = results.average("age");


long   matches = results.size();

还有其他的一些

等等等等。更多的可以查看该网址 Realm Api

3.6 改

只要写入都是需要在事务中操作。先查询再修改

mRealm.executeTransaction(new Realm.Transaction() {

    @Override

    public void execute(Realm realm) {

        //先查找后得到User对象

        User user = mRealm.where(User.class).findFirst();

        user.setName("John")

    }

});

3.7 删

删除还是很简单的,不过需要得到数据后才能删除。

final RealmResults<Dog> results = realm.where(Dog.class).findAll();


// All changes to data must happen in a transaction

realm.executeTransaction(new Realm.Transaction() {

    @Override

    public void execute(Realm realm) {

        // remove single match

        results.deleteFirstFromRealm();

        results.deleteLastFromRealm();


        // remove a single object

        Dog dog = results.get(5);

        dog.deleteFromRealm();


        // Delete all matches

        results.deleteAllFromRealm();

    }

});

3.8 版本升级(数据迁移)

Realm的其他操作都很简单。就是数据版本升级最麻烦,当数据库结构发生改变时,就需要升级版本了。

当然你也可以直接删掉目前存在的版本(一般用在前期开发阶段),这样子就不用更新了

使用方法如下:

RealmConfiguration config = new RealmConfiguration.Builder()

    .deleteRealmIfMigrationNeeded()

    .build()

需要如下两个步骤

1.设置新的架构版本设置新的架构版本,在原版本代码+1,如老版本1,新版本2.并且设置migration 代码类

RealmConfiguration config = new RealmConfiguration.Builder()

    .schemaVersion(2) // Must be bumped when the schema changes

    .migration(new MyMigration()) // Migration to run instead of throwing an exception

    .build()

2. 例子(包括MyMigration类)

版本1开始,没有Person类,我先创建Person

public class Person extends RealmObject {


    private String name;


    public void setDog(RealmList<Dog> dog) {

        mDog = dog;

    }


    public String getName() {

        return name;

    }


    public void setName(String name) {

        this.name = name;

    }

}

版本2 让name属性设置required注解

public class Person extends RealmObject {


    @Required

    private String name;


    public void setDog(RealmList<Dog> dog) {

        mDog = dog;

    }


    public String getName() {

        return name;

    }


    public void setName(String name) {

        this.name = name;

    }


}

版本三:创建Dog类,并且再Person添加Dog对象,以及List类型

public class Person extends RealmObject {



    @Required

    private String name;


    private RealmList<Dog> mDog;


    private Dog mDogObject;



    public Dog getDogObject() {

        return mDogObject;

    }


    public void setDogObject(Dog dogObject) {

        mDogObject = dogObject;

    }


    public RealmList<Dog> getDog() {

        return mDog;

    }


    public void setDog(RealmList<Dog> dog) {

        mDog = dog;

    }


    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

}


public class Dog extends RealmObject {


    private String name;


    public String getName() {

        return name;

    }


    public void setName(String name) {

        this.name = name;

    }

}
public class MyMigration implements RealmMigration {

    @Override

    public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {

        RealmSchema realmSchema = realm.getSchema();


        if (oldVersion == 1) {

            realmSchema.create("Person")

                    .addField("name", String.class);


            oldVersion++;

        }


        if (oldVersion == 2) {

            realmSchema.get("Person")

                    .setRequired("name", true)

                    .transform(new RealmObjectSchema.Function() {

                        @Override

                        public void apply(DynamicRealmObject obj) {

                            DynamicRealmObject person = realm.createObject("Person");

                            person.set("name", "John");



                        }

                    });

            oldVersion++;

        }


        if (oldVersion == 3) {


            RealmObjectSchema realmObjectSchema = realmSchema.create("Dog")

                    .addField("name", String.class);


            realmSchema.get("Person")

                    .addRealmObjectField("mDogObject", realmObjectSchema)

                    .addRealmListField("mDog", realmObjectSchema);

            oldVersion++;

        }

    }

}

realmSchema类其他用法

  • schema.setNullable("name", true): 取消name必填
  • shema.removeField("name"):移除name
  • schema.renameField("name","personName"):重命名

3.9 加密

请注意我们许可证的“出口合规”部分,因为如果您位于有美国出口限制或禁运的国家/地区,
它会限制使用Realm。
通过将512位加密密钥(64字节)传递给配置,可以在磁盘上对Realm文件进行加密RealmConfiguration.Builder.encryptionKey:


byte[] key = new byte[64];

new SecureRandom().nextBytes(key);

RealmConfiguration config = new RealmConfiguration.Builder()
  .encryptionKey(key)
  .build();


Realm realm = Realm.getInstance(config);

当给出加密密钥时,Realm使用标准AES-256加密透明地加密和解密数据。每次打开Realm时都必须
提供相同的加密密钥。有关如何在Android KeyStore中的运行之间安全存储密钥以便其他应用程序
无法读取它们的示例,请参阅 加密例子

四:问题

主键(包括自增长)

Realm不支持自动增量ID。这主要是因为不可能在分布式环境中生成这样的密钥,并且存储在本地Realm和同步Realm中的数据之间的兼容性是高优先级

不是自增长

官方提供的解决方法是:

1.用GUID代替,它可以保证唯一性,即使在设备离线时也可以由设备创建:GUID代替,它可以保证唯一性,即使在设备离线时也可以由设备创建:

2.或者用Date来保证唯一性

public class Person extends RealmObject {

    @PrimaryKey

    private String id = UUID.randomUUID().toString();

     private Date createdAt = new Date();

    private String name;

}

自增长

Number maxValue = realm.where(MyObject.class).max("primaryKeyField");

long pk = (maxValue != null) ? maxValue + 1 : 0;

支持SQL

这个是不是SQlite哦,所以不支持。

 

Intent传递:

因为RealmObjects不能直接传递,可以传递主键,接着在另外一个地方查询该条。

 

五:配合使用

rxjava

他是一个可选的依赖性,Realm不会自动包含他,这样的好处是,你可以选择使用那个版本的Rxjava,所以你要自己在build.gradle里添加

Realm realm = Realm.getDefaultInstance();
GitHubService api = retrofit.create(GitHubService.class);
realm.where(Person.class).isNotNull("username").findAllAsync().asObservable()
    .filter(persons.isLoaded)
    .flatMap(persons -> Observable.from(persons))
    .flatMap(person -> api.user(person.getGithubUserName())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(user -> showUser(user));

例子:https://github.com/realm/realm-java/tree/master/examples/encryptionExample

多线程例子

threadExample

加密

encryptionExample

单元测试

unitTestExample

多进程

multiProcessExample

适配器

adapterExample

JSON

jsonExample

等等

六 :Demo

Demo

如有错误请指出 ,谢谢咯

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值