Realm数据库详细介绍
目录
0.官方简介
1.基本概念
2.使用方法流程
1.添加Realm到工程
2.创建一个Realm
3.创建一个RealmObject
4. 创建transaction
5.书写查询
3.实战演练
1.添加数据
2.查询数据
3.更新数据
4.删除数据
4.进阶用法
1.用json创建对象
2.数据模型改变的处理
3.与RxJava的结合
5.其他补充
1.在Application做初始化配置
2. RealmConfiguration类的说明翻译:
3.初始化数据
4.条件查询方法
6.其他介绍【异步操作 】
1.异步增
2.异步删
3.异步改
4.异步查
0.官方简介
-
- Realm实例不能在不同的线程间访问操作。确切的说,你必须在每个要使用的线程上打开一个实例 每个线程都会使用引用计数来自动缓存Realm实例,所以只要引用计数不达到零, 调用getInstance(RealmConfiguration)方法将会返回缓存的Realm实例,应该算是一个轻量级的操作。
- 对于UI线程来说,打开和关闭Realm实例,应当放在onCreate/onDestroy或者onStart/onStop方法中
- 在不同的线程间,Realm实例使用Handler机制来调整他的状态。也就是说,Realm实例在线程中,如果没有Looper,是不能收到更新通知的。除非手动调用waitForChange()方法
1.基本概念
-
- realm是一个跨平台移动数据库引擎,支持iOS、OS X(Objective-C和Swift)以及Android。
- 因为是ORM,本身在设计时也针对移动设备(iOS、Android),所以非常简单易用,学习成本很低。
- Realm是一个可以替代SQLite以及ORMlibraries的轻量级数据库。
- 相比SQLite,Realm更快并且具有很多现代数据库的特性,比如支持JSON,流式api,数据变更通知,以及加密支持,这些都为安卓开发者带来了方便。
2.使用方法流程
1.添加Realm到工程
第一种方法
要在安卓工程中使用Realm,你需要在module的build.gradle文件中添加一个添加一个依赖:compile 'io.realm:realm-android:0.84.1'
第二种方法
在项目的build文件加上
buildscript {
repositories {
jcenter()
}
dependencies {
...
classpath "io.realm:realm-gradle-plugin:1.2.0"
}
在 app 的 build文件中加入
apply plugin: 'realm-android'
2.创建一个Realm
一个Realm相当于一个SQLite数据库。它有一个与之对应的文件,一旦创建将持久保存在安卓的文件系统中。
要创建一个新的Realm,你可以在任意Activity中调用静态方法Realm.getInstance。
Realm myRealm = Realm.getInstance(context);
注意,调用Realm.getInstance,而没有传入RealmConfiguration,会创建一个叫做 default.realm的Realm文件。
如果你想向app中添加另一个Realm,必须使用一个RealmConfiguration.Builder对象,并为 Realm file 指定一个独有的名字。
Realm myOtherRealm = Realm.getInstance(new RealmConfiguration.Builder(context).name("myOtherRealm.realm").build());
3.创建一个RealmObject
只要继承了RealmObject类,任意JavaBean都能存储在Realm中。不知道JavaBean是什么?它就是一个可序列化的java类,有默认构造器,成员变量有相应的getter/setter方法。比如,下面这个类的实例就能轻松的存储在一个Realm中:
public class Country extends RealmObject {
private String name;
private int population;
public Country() { }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
}
多对多的关系
public class Contact extends RealmObject {
public String name;
public RealmList<Email> emails;
}
public class Email extends RealmObject {
public String address;
public boolean active;
}
如果你想让RealmObject的一个成员变量作为主键,你可以使用@PrimaryKey注解。比如,这里演示了如何为Country类添加一个主键code:
private String code;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
-
-
@PrimaryKey
用来标识主键- 默认的所有的字段都会被存储
- 如果某个字段不需要被存储到本地,则需在在这个字段上面加上
@Ignore
注解 - @Index 为这个字段添加一个搜索引擎,这将使插入数据变慢、数据增大,但是查询会变快。建议在需要优化读取性能的情况下使用。
-
4. 创建transaction
虽然从一个Realm读取数据非常简单(下一节有讲),但是向它写入数据就稍微复杂一点。Realm遵循 ACID (数据库事务正确执行的四个基本要素的缩写)规范,为了确保原子性和一致性,它强制所有的写入操作都在一个事务中执行。
-
-
-
- 开始一个新的事务,使用beginTransaction方法。
- 结束这个事务,使用commitTransaction方法。
-
-
注:事务即英文里面的transaction
这里演示了如何创建和保存一个Country类的实例:
类型一 :新建一个对象,并进行存储
Realm myRealm = Realm.getDefaultInstance();
myRealm.beginTransaction();
Country country1 = myRealm.createObject(Country.class);
country1.setName("Norway");
country1.setPopulation(5165800);
country1.setCode("NO");
myRealm.commitTransaction();
你可能注意到了country1并不是用Country类的构造器创建的。对于一个Realm来说,管理一个RealmObject的实例,这个实例必须用createObject方法创建。
如果你必须使用构造器,别忘了在提交事务前使用相关Realm对象的copyToRealm方法。这里是示例:
类型二:复制一个对象到Realm数据库
Realm myRealm = Realm.getDefaultInstance();
Country country2 = new Country();
country2.setName("Russia");
country2.setPopulation(146430430);
country2.setCode("RU");
myRealm.beginTransaction();
Country copyOfCountry2 = myRealm.copyToRealm(country2);
myRealm.commitTransaction();
类型三:使用事物块
Realm mRealm=Realm.getDefaultInstance();
final User user = new User("John");
user.setEmail("john@corporation.com");
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.copyToRealm(user);
}
});
5.书写查询
Realm为创建查询提供了一套非常直观和流式的API。要创建一个查询,使用相关Realm对象的where方法并传入你感兴趣的对象的类。创建完查询之后,你可以使用返回一个RealmResults对象的findAll方法获取所有的结果,findAll。
RealmResults<Country> results1 = myRealm.where(Country.class).findAll();
for(Country c:results1) {
Log.d("results1", c.getName());
}
Realm提供了几个命名非常贴切的方法,比如beginsWith, endsWith,lesserThan 和 greaterThan,可以用来过滤,筛选结果。
RealmResults<Country> results2 =myRealm.where(Country.class).greaterThan("population", 100000000).findAll();
如果你想查询结果被归类,你可以使用findAllSorted方法。在它的参数中,用一个String指定被归类field的名字,并用一个boolean指定归类顺序。
RealmResults<Country> results3 = myRealm.where(Country.class).findAllSorted("name", false);
3.实战演练
数据库的使用无非上就是增删改查这四种操作,其中查是重点,在写原生sql语句中这也是个难点。
1.添加数据
在用Realm进行操作之前需要对Realm作相关的配置操作,Realm中所有的写操作都必须在事务中进行,不然就会报错,记得在Activity的onDestory中调用realm.close()释放资源。
RealmConfiguration realmConfig = new RealmConfiguration.Builder(this).build();
Realm realm = Realm.getInstance(realmConfig);
public void testAdd() {
initRealm();
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
for (int i = 0; i < 10; i++) {
User user = realm.createObject(User.class);
user.setName("user" + i);
user.setAge(10 + i);
user.setId(UUID.randomUUID().toString());
}
showInTextView("10条数据添加成功");
}
});
}
//记得一定要在onDestroy方法中释放资源, 否则会导致本地资源无法释放,引起OOM。
@Override
protected void onDestroy() {
super.onDestroy();
realm.close();
}
2.查询数据
查询全部
public void testQuery() {
List<User> users= realm.where(User.class).findAll();
for (User user: users) {
showInTextView("id:" + user.getId() + " name:" + user.getName() + " age:" + user.getAge());
}
}
条件查询,Realm 支持以下查询条件(来源于官网):
-
-
- between()、greaterThan()、lessThan()、greaterThanOrEqualTo() 和 lessThanOrEqualTo()
- equalTo() 和 notEqualTo()
- contains()、beginsWith() 和 endsWith()
- isNull() 和 isNotNull()
- isEmpty() 和 isNotEmpty()
-
以下代码片段查询年龄小于15的User
public void testQueryAgeLessThan15() {
List<User> users= realm.where(User.class).lessThan("age", 15).findAll();
for (User user: users) {
showInTextView("id:" + user.getId() + " name:" + user.getName() + " age:" + user.getAge());
}
}
聚合查询,支持的聚合操作有sum,min,max,average
public void testQueryAverageAge() {
double age = realm.where(User.class).findAll().average("age");
textView.setText("average age:" + age);
}
3.更新数据
public void testUpdate() {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User user = realm.where(User.class).equalTo("name", "user9").findFirst();
if (user != null) {
user.setAge(99);
user.setName("二逼青年");
}
textView.setText("更新成功");
}
});
}
4.删除数据
以下代码片段展示了如何删除指定的对象
public void testDelete() {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User user = realm.where(User.class).equalTo("name", "user0").findFirst();
if (user != null)
user.deleteFromRealm();
textView.setText("删除成功");
}
});
}
4.进阶用法
1.用json创建对象
在实际开发中我们和json打交道的机会比较多,所以直接从json去创建对象是十分有用的,下面的代码片段展示了怎么去用。
private void testAddFromJson() {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
String json = "{\n" +
" \"id\": \"uuid1\",\n" +
" \"name\": \"solid\",\n" +
" \"age\": 20\n" + "}";
String jsons = "[\n" +
" {\n" +
" \"id\": \"uuid1\",\n" +
" \"name\": \"solid\",\n" +
" \"age\": 20\n" +
" },\n" +
" {\n" +
" \"id\": \"uuid2\",\n" +
" \"name\": \"jhack\",\n" +
" \"age\": 21\n" +
" },\n" +
" {\n" +
" \"id\": \"uuid3\",\n" +
" \"name\": \"tom\",\n" +
" \"age\": 22\n" +
" }\n" +
"]";
//realm.createObjectFromJson(User.class, json);
realm.createAllFromJson(User.class, jsons);
}
});
}
2.数据模型改变的处理
开发中数据模型不可能从一开始创建了,就保证后面的开发过程中不会更改,对于Realm如果其中的某个实体类改变了,而我们没有做任何的处理,就会报错,如果还处于应用的开发的初期,这无所谓,直接清空数据即可,但是如果应用已经发布了,我们就需要去寻找一种解决方案了。
public class MyMigration implements RealmMigration {
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
Log.e(MainActivity.TAG, "oldVersion:" + oldVersion + " newVersion:" + newVersion);
RealmSchema schema = realm.getSchema();
if (newVersion == 2) {
schema.get("User")
.addField("desc", String.class);
}
}
@Override
public boolean equals(Object obj) {
return obj instanceof MyMigration;
}
@Override
public int hashCode() {
return super.hashCode();
}
}
realmConfig = new RealmConfiguration
.Builder(this)
.schemaVersion(2)
.migration(new MyMigration())
.build();
3.与RxJava的结合
Realm原生是支持Rxjava的,由于RxJava 是可选依赖,所以在使用的时候需要在app的build文件中添加RxJava库的依赖,下面是使用的代码片段。
public void testRxJava() {
realm.where(User.class).findAll()
.asObservable()
.flatMap(new Func1<RealmResults<User>, Observable<User>>() {
@Override
public Observable<User> call(RealmResults<User> users) {
return Observable.from(users);
}
})
.subscribe(new Action1<User>() {
@Override
public void call(User user) {
showInTextView("id:" + user.getId() + " name:" + user.getName() + " age:" + user.getAge() + " desc:" + user.getDesc());
}
});
}
5.其他补充
1.在Application做初始化配置
1.使用默认配置
RealmConfiguration realmConfig = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(realmConfig);
2.使用自定义配置
RealmConfiguration config = new RealmConfiguration.Builder(this)
.name("myRealm.realm")
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(config);
3.记得到清单文件配置
<application
android:name=".MyApplication"
...
/>
2. RealmConfiguration类的说明翻译:
/**
* 一个RealmConfiguration对象,可用来设置特定的Realm实例
* RealmConfiguration实例只能通过io.realm.RealmConfiguration.Builder类的build()方法来创建
* 想使用默认的RealmConfiguration实例,请使用io.realm.Realm#getDefaultInstance()方法。
* 如果想使用自己配置RealmConfiguration实例的Realm实例,需要调用Realm#setDefaultConfiguration(RealmConfiguration)
*
* <p>
* 可以用下面代码创建一个最简单配置的实例:
* RealmConfiguration config = new RealmConfiguration.Builder(getContext()).build())
* 这样创建的实例,具有一下属性:
*
* <ul>
* <li>Realm的默认文件名是"default.realm"</li>
* <li>"default.realm"文件保存在"Context.getFilesDir()"目录中</li>
* <li>它的schema版本号设置为0</li>
* </ul>
*/
解释:
1.即默认文件目录:/data/data/<packagename>/files/default.realm。意思是,当你在应用管理里面给当前app"清除数据",realm数据库的数据会丢失。
2.可以设置默认文件名,通过RealmConfiguration类进行配置。路径似乎改不了,需要看具体设备供应商的实现。
3. Realm的实例需要在每次的具体操作中获取,可以看成是一个数据操作的sessin,用完后必须close关闭。
4. 重点:切记,Realm数据库的主键字段不是自动增长的,需要自己设置,做添加的时候如果不给id字段值,默认会为0。
5. 数据自动更新。mRealm.addChangeListener(this);//当数据库的数据有变化时,系统回调此方法。
3.初始化数据
//init data
public boolean init() {
/**
* 此处要注意,方法最后调用的是添加或者修改的方法。
* 如果list的数据都不给id,则第一条记录添加成功后的id为0,后面的都在此基础上修改。
* 最后的效果是,数据库只有一条记录,id为0,其他字段被更新为了最后一个对象的数据
*/
List<TestUser> list = new ArrayList<>();
list.add(new TestUser(0,"android", "123123", 20, "河南常德", "传菜员", "女"));
list.add(new TestUser(1,"angel", "13588889988", 21, "云南西双版纳", "飞行员", "男"));
list.add(new TestUser(2,"adidass", "110119", 28, "云南德克萨斯州", "海员", "男"));
list.add(new TestUser(3,"hijack", "250250", 39, "加州电厂", "厨师", "女"));
list.add(new TestUser(4,"hibrid", "120250", 26, "赣州", "贼", "男"));
list.add(new TestUser(5,"admin", "123456", 20, "湖北汉城", "程序员", "女"));
return saveOrUpdateBatch(list);
}
4.条件查询方法
/**
* 条件查询
*
* @return 返回结果集合
*/
public RealmResults<TestUser> findByAnyParams(HashMap<Object, Object> params) {
//realm.where(TestUser.class)
//可跟查询条件
//.or() 或者
//.beginsWith() 以xxx开头
//.endsWith() 以xxx结尾
//.greaterThan() 大于
//.greaterThanOrEqualTo() 大于或等于
//.lessThan() 小于
//.lessThanOrEqualTo() 小于或等于
//.equalTo() 等于
//.notEqualTo() 不等于
//.findAll() 查询所有
//.average() 平均值
//.beginGroup() 开始分组
//.endGroup() 结束分组
//.between() 在a和b之间
//.contains() 包含xxx
//.count() 统计数量
//.distinct() 去除重复
//.findFirst() 返回结果集的第一行记录
//.isNotEmpty() 非空串
//.isEmpty() 为空串
//.isNotNull() 非空对象
//.isNull() 为空对象
//.max() 最大值
//.maximumDate() 最大日期
//.min() 最小值
//.minimumDate() 最小日期
//.sum() 求和
return realm.where(TestUser.class).findAll();
}
6.其他介绍
异步操作
大多数情况下,Realm的增删改查操作足够快,可以在UI线程中执行操作。但是如果遇到较复杂的增删改查,或增删改查操作的数据较多时,就可以子线程进行操作。
(1)异步增:
private void addCat(final Cat cat) {
RealmAsyncTask addTask= mRealm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.copyToRealm(cat);
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
ToastUtil.showShortToast(mContext,"收藏成功");
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
ToastUtil.showShortToast(mContext,"收藏失败");
}
});
}
最后在销毁Activity或Fragment时,要取消掉异步任务
@Override
protected void onDestroy() {
super.onDestroy();
if (addTask!=null&&!addTask.isCancelled()){
addTask.cancel();
}
}
(2)异步删
private void deleteCat(final String id, final ImageView imageView){
RealmAsyncTask deleteTask= mRealm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Cat cat=realm.where(Cat.class).equalTo("id",id).findFirst();
cat.deleteFromRealm();
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
ToastUtil.showShortToast(mContext,"取消收藏成功");
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
ToastUtil.showShortToast(mContext,"取消收藏失败");
}
});
}
最后在销毁Activity或Fragment时,要取消掉异步任务
@Override
protected void onDestroy() {
super.onDestroy();
if (deleteTask!=null&&!addTask.isCancelled()){
deleteTask.cancel();
}
}
(3)异步改
RealmAsyncTask updateTask= mRealm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Cat cat=realm.where(Cat.class).equalTo("id",mId).findFirst();
cat.setName(name);
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
ToastUtil.showShortToast(UpdateCatActivity.this,"更新成功");
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
ToastUtil.showShortToast(UpdateCatActivity.this,"失败成功");
}
});
最后在销毁Activity或Fragment时,要取消掉异步任务
@Override
protected void onDestroy() {
super.onDestroy();
if (updateTask!=null&&!addTask.isCancelled()){
updateTask.cancel();
}
}
(4)异步查
RealmResults<Cat> cats=mRealm.where(Cat.class).findAllAsync();
cats.addChangeListener(new RealmChangeListener<RealmResults<Cat>>() {
@Override
public void onChange(RealmResults<Cat> element) {
element= element.sort("id");
List<Cat> datas=mRealm.copyFromRealm(element);
}
});
最后在销毁Activity或Fragment时,要取消掉异步任务
@Override
protected void onDestroy() {
super.onDestroy();
cats.removeChangeListeners();
}