导图社区 Realm数据库
Realm数据库的学习增删改查与常见问题的描述解决方案
编辑于2019-09-30 13:28:55Realm数据库
realm是一个跨平台移动数据库引擎,支持iOS、OS X(Objective-C和Swift)以及Android; 核心数据引擎由C++打造,并不是建立在SQLite之上的ORM, 拥有独立的数据库存储引擎; 据官方称性能上比sqlite, coredata牛逼,而且使用起来更加简单, 更易入门
引入Realm
在你项目的build.gradle中: dependencies { classpath 'com.android.tools.build:gradle:3.0.0-beta6' // classpath "io.realm:realm-gradle-plugin:4.3.1" //stetho_realm不支持高版本,这里使用3.0.0 classpath "io.realm:realm-gradle-plugin:3.0.0" } 在你app的build.gradle中: apply plugin: 'realm-android'
在你项目的build.gradle中: repositories { maven { url 'https://github.com/uPhyca/stetho-realm/raw/master/maven-repo' } } 在你app的build.gradle中: compile 'com.facebook.stetho:stetho:1.5.0' compile 'com.uphyca:stetho_realm:2.1.0'
初始化Realm
Builder.name : 指定数据库的名称。如不指定默认名为default。 Builder.schemaVersion : 指定数据库的版本号。 Builder.encryptionKey : 指定数据库的密钥。 Builder.migration : 指定迁移操作的迁移类。 Builder.deleteRealmIfMigrationNeeded : 声明版本冲突时自动删除原数据库。 Builder.inMemory : 声明数据库只在内存中持久化。 build : 完成配置构建。
1. 子主题
2. 子主题
3. 子主题
4. 子主题
5. 子主题
获取Realm的实例 return Realm.getInstance(MyApplication.getRealmConfiguration());
子主题
子主题
构建数据库model
方式一:继承RealmObject
方式二:实现 RealmModel接口并添加 @RealmClass修饰符来声明
关闭数据库
if (mRealm != null && !mRealm.isClosed()) { mRealm.close(); }
数据库操作
增加数据
同步操作 [ realm.executeTransaction]
createObject [realm.createObject(Student.class)]
增加一对一表,Person包含Men { mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { Person person = realm.createObject(Person.class); person.setCity("杭州"); person.setProject("android"); Men men = realm.createObject(Men.class); men.setName("huangxiaoguo"); men.setAge(25); person.setMen(men); } });
增加一对多表,Person包含多个Men{ 注意:在Realm中不能使用android原有的list集合,需要Realm特定的RealmList集合! mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { MorePerson morePerson = realm.createObject(MorePerson.class); morePerson.setCity("杭州"); morePerson.setProject("开发工程师"); morePerson.setTime(simpleDateFormat.format(new Date())); for (int i = 0; i < 3; i++) { Men men = realm.createObject(Men.class); men.setName("huangxiaoguo" + i); men.setAge(new Random().nextInt(100)); morePerson.getMens().add(men); } } });
copyToRealmOrUpdate [realm.copyToRealmOrUpdate(user)] * 当Model中存在主键的时候,推荐使用copyToRealmOrUpdate方法插入数据。 * 如果对象存在,就更新该对象;反之,它会创建一个新的对象。
增加一对一表,Person包含一个Men { final Person person1 = new Person(); person1.setCity("杭州"); person1.setProject("android"); Men men1 = new Men(); men1.setName("huangxiaoguo"); men1.setAge(new Random().nextInt(100)); person1.setMen(men1); mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { //如果使用copyToRealmOrUpdate,Person需要主键 realm.copyToRealm(person1); } });
增加一对多表,Person包含多个Men{ final MorePerson morePerson1 = new MorePerson(); morePerson1.setCity("杭州"); morePerson1.setProject("开发工程师"); morePerson1.setTime(simpleDateFormat.format(new Date())); RealmList<Men> menList = new RealmList<>(); for (int i = 0; i < 3; i++) { Men men2 = new Men(); men2.setName("huangxiaoguo" + i); men2.setAge(new Random().nextInt(100)); menList.add(men2); } morePerson1.setMens(menList); for (int i = 0; i < 3; i++) { Men men2 = new Men(); men2.setName("huangxiaoguo" + i); men2.setAge(new Random().nextInt(100)); morePerson1.getMens().add(men2); } mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.copyToRealm(morePerson1); } });}
copyToRealm [realm.copyToRealm(student)] *若该Model没有主键,使用copyToRealm方法,否则将抛出异常。
异步操作[executeTransactionAsync] //该方法会开启一个子线程来执行事务, 并且在执行完成后进行结果通知。
异步操作需要在activity不可见或关闭时取消任务 @Override protected void onDestroy() { super.onDestroy(); if (realmAsyncTask != null && !realmAsyncTask.isCancelled()) { realmAsyncTask.cancel(); } if (mRealm != null && !mRealm.isClosed()) { mRealm.close(); } }
异步操作:使用executeTransactionAsync并且进行监听 //注意:如果当Acitivity或Fragment被销毁时,在OnSuccess或OnError中执行UI操作, // 将导致程序奔溃 。用RealmAsyncTask .cancel();可以取消事务 //在onStop或onDestroy中调用,避免crash
查询数据
同步操作:findAll查询 * 注意:RealmResults虽然实现了List接口,不过有很多方法是不能用的。 * 比如add、addAll、remove、clear等, * 调用后会直接抛异常。 * 因为它们都被标记为@Deprecated了 RealmResults<Men> menList = mRealm.where(Men.class) .findAll();
异步操作:findAllAsync查询(isLoaded) RealmResults<Men> menList1 = mRealm.where(Men.class) .findAllAsync(); menList1.load();//等到查询完成。这将阻塞该线程,使查询再次同步 if (menList1 != null && menList1.isLoaded()) { for (int i = 0; i < menList1.size(); i++) { Log.d("huangxiaoguo", "huangixoaguo==>" + menList1.get(i).getName() + "----" + menList1.get(i).getAge()); } }
异步操作:findAllAsync查询(RealmChangeListener) menList2 = mRealm.where(Men.class).findAllAsync(); menList2.addChangeListener(callback); */ private OrderedRealmCollectionChangeListener<RealmResults<Men>> callback = new OrderedRealmCollectionChangeListener<RealmResults<Men>>() { @Override public void onChange(RealmResults<Men> collection, OrderedCollectionChangeSet changeSet) { if (changeSet == null) { //第一次异步返回一个空的变更集。 for (int i = 0; i < collection.size(); i++) { Log.d("huangxiaoguo", "huangixoaguo==>" + collection.get(i).getName() + "----" + collection.get(i).getAge()); } } else { //每次RealmResults被更新时都会被调用 } } };
使用异步查询 要在actiity销毁时候置空listenner跟realm @Override protected void onDestroy() { super.onDestroy(); if (menList2 != null) { menList2.removeChangeListener(callback); menList2.removeAllChangeListeners(); } if (mRealm != null && !mRealm.isClosed()) { mRealm.close(); } }
聚合{ /** * sum():对指定字段求和。 average():对指定字段求平均值。 min(): 对指定字段求最小值。 max() : 对指定字段求最大值。count : 求结果集的记录数量。 findAll(): 返回结果集所有字段,返回值为RealmResults队列 findAllSorted() : 排序返回结果集所有字段,返回值为RealmResults队列 between() equalTo() & notEqualTo() contains(), beginsWith() & endsWith() isNull() & isNotNull() isEmpty()& isNotEmpty() */
更新数据 重要:使用查询语句得到数据,然后将内容修改
同步操作:executeTransaction修改
mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { Men men = realm.where(Men.class).findFirst(); men.setName("我把第一个同步修改了"); } });
异步操作:executeTransactionAsync修改
realmAsyncTask = mRealm.executeTransactionAsync(new Realm.Transaction() { @Override public void execute(Realm realm) { Men men = realm.where(Men.class).findFirst(); men.setName("我把第一个,异步修改了"); } }, new Realm.Transaction.OnSuccess() { @Override public void onSuccess() { UIUtils.showToast("修改完成"); } }, new Realm.Transaction.OnError() { @Override public void onError(Throwable error) { UIUtils.showToast("修改失败"); } });
@Override protected void onDestroy() { super.onDestroy(); if (realmAsyncTask != null && !realmAsyncTask.isCancelled()) { realmAsyncTask.cancel(); } if (mRealm != null && !mRealm.isClosed()) { mRealm.close(); } }
子主题
删除数据
同步删除(一):先查找到数据:deleteFromRealm(int index)
删除指定数据 final RealmResults<Student> students = mRealm.where(Student.class).findAll(); mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { students.deleteFromRealm(0); } });
同步删除(一):先查找到数据:deleteFirstFromRealm()
删除第一条数据 final RealmResults<Student> students1 = mRealm.where(Student.class).findAll(); mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { students1.deleteFirstFromRealm(); } });
同步删除(一):先查找到数据:deleteAllFromRealm()
删除全部数据 final RealmResults<Student> students3 = mRealm.where(Student.class).findAll(); mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { students3.deleteAllFromRealm(); } });
同步删除:deleteAll()
//所有的数据库都会清除 mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.deleteAll(); } });
同步删除:delete(xxx.class)
//只会删除指定的数据库 mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.delete(Student.class); } });
异步删除(推荐使用):delete(xxx.class)
推荐使用:异步查询,同步删除
数据迁移
1.创建CustomMigration(根据新旧字段变化进行逻辑编写)
public class CustomMigration implements RealmMigration { @Override public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) { RealmSchema schema = realm.getSchema(); if (oldVersion == 0 && newVersion == 1) { schema.get("MigrationPerson") .removePrimaryKey()//去除主键标志 .removeField("sex")//删除sex字段 .renameField("age", "myage")//更改age字段为myage字段 .addField("num", int.class)//添加num字段int类型 .addField("project", String.class)//添加project字段int类型, // .addField("id", long.class, FieldAttribute.PRIMARY_KEY)//使用三个参数的,可设置注释属性 .addRealmObjectField("men", schema.get("Men"))//添加对象 .addRealmListField("mens", schema.get("Men"))//添加集合对象 .transform(new RealmObjectSchema.Function() { @Override public void apply(DynamicRealmObject obj) { DynamicRealmObject men = realm.createObject("Men"); men.set("name", "迁移Men"); men.set("age", 8); obj.setObject("men", men);//初始化值的操作 obj.getList("mens").add(men); } }); //注意这里要++,因为Realm不会自动为其增加 oldVersion++; } } }
2.更改Realm初始配置
Realm.init(this); instance = this; new SecureRandom().nextBytes(UIUtils.getRealmKey(key)); config = new RealmConfiguration.Builder() .name("huangxiaoguo.realm")//指定数据库的名称。如不指定默认名为default .encryptionKey(UIUtils.getRealmKey(key))//指定数据库的密钥。 .schemaVersion(1) // .deleteRealmIfMigrationNeeded()//声明版本冲突时自动删除原数据库,开发时候打开 .migration(new CustomMigration())//指定迁移操作的迁移类。 // .inMemory()// 声明数据库只在内存中持久化 .build();
数据库加密
Realm 文件可以通过传递一个512位(64字节)的密钥参数给 Realm.getInstance().encryptionKey() 来加密存储在磁盘上。
因为每次创建新的 Realm 实例的时候,都需要提供相同的密钥,所以如果使用官方随机产生的话,第二次打开app数据库可能会禁止访问!
Realm常见问题
1.Object not managed by Realm, so it cannot be removed Realm不支持直接通过deleteFromRealm删除Bean类,即使该Bean extends RealmObject,否则会报此异常
final RealmResults<Student> students3 = mRealm.where(Student.class).findAll(); mRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { students3.deleteAllFromRealm(); } });
2.Realm accessed from incorrect thread
RealmObject自带线程保护功能, 只能在创建它的线程中访问, 在子线程中不能访问. 也就是说,如果你在主线程中new了一个RealmObject对象 user,那么在子线程中是访问不了user对象的。 要想在子线程中访问,必须先将user存入Ream中,然后在子线程中query出来。
3.is not part of the schema for this Realm
需要调整plugin中的顺序,如下: apply plugin: 'com.android.application' apply plugin: 'com.bugtags.library.plugin' apply plugin: 'android-apt' apply plugin: 'realm-android' apply plugin: 'com.neenbedankt.android-apt'
4.异步查询之坑
1.官方文档介绍 主线程操作Realm会卡顿/阻塞线程 官方表示Realm运行速度很快,足以在主线程运行,而后又表示其实还是会阻塞线程导致偶发的ANR,因此建议在子线程操作Realm.
2.子线程查询的数据,无法在主线程使用
解决方案: 子线程查询,置换为自己的Bean类,然后在主线程使用.
3.RejectedExecutionException
解决方案: 不要在for循环中使用Realm,将数据存入集合中,然后开启事务,直接使用copyToRealmOrUpdate(realmObjectList)存储即可.
4.事务嵌套报异常
原因 : 在一个事务中开启了另外一个事务.应避免这种情况.
try (Realm realm = Realm.getDefaultInstance()) { // No need to close the Realm instance manually }
Realm使用注意事项 1.Realm默认运行在主线程中,使用时要在异步线程中使用. 2.Realm本身是单利类,可以多线程并发调用,但是RealmObject则不允许并发,每个RealmObject都绑定自己的TreadId.必须在创建该RealmObject中的线程中使用. 3.在子线程中查询出来的数据无法在主线程中使用,解决方案是 在子线程中查询 ,置换为自己的Bean类 ,然后再主线程中使用. 4.没有主键的RealmObject没有办法update操作, 如果要使用必须要设置主键 . 5.如果Realm关闭 ,则查询出来的RealmObject数据都无法使用 . 解决方案是复制一份数据到本地内存中. 6.数据库操作必须在transtation中使用, 要开启事务.