Realm数据库
Realm数据库是一个跨平台移动数据库引擎,支持iOS、OS X(Objective-C和Swift)以及Android。
2014年7月发布。由YCombinator孵化的创业团队历时几年打造,是第一个专门针对移动平台设计的数据库。目标是取代SQLite。
为了彻底解决性能问题,核心数据引擎C++打造,并不是建立在SQLite之上的ORM。如果对数据引擎实现想深入了解可以查看:Realm 核心数据库引擎探秘。因此得到的收益就是比普通的ORM要快很多,甚至比单独无封装的SQLite还要快。
Realm官网:https://realm.io/cn/
Realm基于ORM,何为ORM呢?
对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换[1] 。从效果上说,它其实是创建了一个可在编程语言里使用的–“虚拟对象数据库”。
对象-关系映射(OBJECT/RELATIONALMAPPING,简称ORM),是随着面向对象的软件开发方法发展而产生的。用来把对象模型表示的对象映射到基于S Q L 的关系模型数据库结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQ L 语句打交道,只需简单的操作实体对象的属性和方法 。O R M 技术是在对象和关系之间提供了一条桥梁,前台的对象型数据和数据库中的关系型的数据通过这个桥梁来相互转化
Android中目前流行的常见ORM框架:ORMLite、Afinal、ActiveAndroid、SugarORM、GreenDao、Realm。一般GreenDao和Realm比较流行,GreenDao是基于SQLite数据库的,是对SQLite数据库的一种封装,然后对外提供API操作,不过使用繁琐,需要自己封装很多操作。Realm不基于SQLite,它是基于C++ 存储引擎的,比SQLite提供了更多的特性和性能提升。据官方介绍,Realm在查询和插入性能比SQLite好,在删除性能比SQLite差。总体来说,Realm性能还是优于SQLite,毕竟Realm技术比SQLite新。
插入操作:在同一个事务里,每秒插入100K条记录(越高代表性能越好)
统计操作:每秒能在100K条数据中进行查询后count的次数(越高代表性能越好)
查询操作:在100K中进行一次遍历查询(越高代表性能越好)
配置
目前的限制:
- 不支持 Android 以外的 Java 环境;
- Android Studio >= 1.5.1 ;
- 较新的 Android SDK 版本;
- JDK 版本 >=7;
支持 Android API 9 以上的所有版本(Android 2.3 Gingerbread 及以上)。 - Realm 作为一个 Gradle 插件来安装需要如下两个步骤:
Step1:在项目目录下的 build.gradle 文件中添加如下 class path 依赖。
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath "io.realm:realm-gradle-plugin:2.2.2"//添加这行
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
Step2:在项目的app目录下的 build.gradle 文件中应用 realm-android 插件。
apply plugin: 'com.android.application'
apply plugin: 'realm-android'//添加这行
初始化
Realm(s) 是我们对数据库的称谓:它包含多个不同的对象,并对应磁盘中的一个文件。在使用之前,需要对 Realm 库进行初始化操作。自定义一个MyApplication,继承Application,重写onCreate方法,并在onCreate方法中对Realm进行初始化
public class MyApplication extends Application {
@Override
public void onCreate(){
super.onCreate();
Realm.init(this);
}
}
在AndroidManifest中声明自定义Application:
<application
android:name="application.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
获取Realm单例
通过Realm.getInstance(this)来访问我们已初始化的realm变量。该静态方法会为你的当前线程返回一个Realm实例,它对应了你Context.getFilesDir()目录中的default.realm文件。
public class MainActivity extends BaseActivity {
private Realm realm;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
realm=Realm.getDefaultInstance();
}
@Override
public void onDestroy(){
super.onDestroy();
realm.close();
}
}
数据模型
Realm数据库里面的模型必须继承RealmObject类或者实现RealmModel接口。Realm 数据模型不仅仅支持 private 成员变量,你还可以使用 public、protected 以及自定义的成员方法。目前不支持 final、transient 和 volatile 修饰的成员变量。构造函数必须为空,也就是不能带参数,可以选择不声明默认无参构造函数。
- Type1:继承自 RealmObject 类
public class User extends RealmObject {
@PrimaryKey
private String id;//声明主键
private String name;
private String intro;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
= name;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
}
RealmObject的所有方法都有其相对应的静态方法。
// With RealmObject
user.isValid();
user.addChangeListener(listener);
- Type2:通过实现 RealmModel 接口并添加 @RealmClass 修饰符来声明
@RealmClass
public class User implements RealmModel {
@PrimaryKey
private String id;//主键
private String name;
private String intro;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
= name;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
}
实现RealmModel 接口的只能调用Realm的静态方法
// With RealmModel
RealmObject.isValid(user);
RealmObject.addChangeListener(user, listener);
字段类型
Realm 支持以下字段类型:boolean、byte、short、int、long、float、double、String、Date和byte []。整数类型 short、int 和 long 都被映射到 Realm 内的相同类型(实际上为 long )。再者,还可以使用 RealmObject 的子类和 RealmList<.? extends RealmObject> 来表示模型关系。
Realm 对象中还可以声明包装类型(boxed type)属性,包括:Boolean、Byte、Short、Integer、Long、Float和Double。通过使用包装类型,可以使这些属性存取空值(null)。
注解字段
- 主键 (@PrimaryKey):@PrimaryKey 可以用来定义字段为主键,该字段类型必须为字符串(String)或整数(short、int 或 long)以及它们的包装类型(Short、Int 或 Long)。不可以存在多个主键。使用支持索引的属性类型作为主键同时意味着为该字段建立索引。<\li>
- 索引(@Index ):注解 @Index 会为字段增加搜索索引。这会导致插入速度变慢,同时数据文件体积有所增加,但能加速查询。因此建议仅在需要加速查询时才添加索引。目前仅支持索引的属性类型包括:String、byte、short、int、long、boolean和Date。<\li>
- 修饰类型和空值(@Required):某些时候,空值(null)对于属性并不合适。这时可以使用注解 @Required 告诉 Realm 强制禁止空值(null)被存储。只有 Boolean、 Byte、 Short、 Integer、 Long、 Float、 Double、 String、 byte[] 以及 Date 可以被 @Required 修饰。在其它类型属性上使用 @Required 修饰会导致编译失败。基本数据类型(primitive types)不需要使用注解 @Required,因为他们本身就不可为空。RealmObject 属性永远可以为空。<\li>
- 忽略属性(@Ignore):注解 @Ignore 意味着一个字段不会被保存到 Realm。某些时候输入的信息包含比模型更多的字段,而你不希望处理这些未使用的数据字段,你可以用 @Ignore 来标识这些你希望被 Realm 忽略的字段。<\li>
创建对象
Realm 对象都强依赖于 Realm,可以直接通过 Realm 被实例化:
- Type1 :使用 realm.createObject:
- 有主键的模型对象创建:
realm.beginTransaction();
User user = realm.createObject(User.class,"1"); // 参数1 模型类名,参数2 主键值(这里指id为1)
user.setName("John");
user.setIntro("Hello World!");
realm.commitTransaction();
没有主键的模型对象创建:
realm.beginTransaction();
User user = realm.createObject(User.class); // 参数1 模型类名
user.setName("John");
user.setIntro("Hello World!");
realm.commitTransaction();
- Type2 :使用 realm.copyToRealm:
可以先创建一个对象的实例,并在之后使用 realm.copyToRealm() 添加。Realm 对象支持多个构造函数,只要其中之一是公共无参数构造函数即可。
User user=new User();
user.setId("1");
user.setName("John");
user.setIntro("Hello World!");
// Copy the object to Realm. Any further changes must happen on realmUser
realm.beginTransaction();
User realmUser = realm.copyToRealm(user);
realm.commitTransaction();
当使用 realm.copyToRealm() 时,请注意只有返回的对象是由 Realm 管理的。只有上面realmUser 是Realm管理的,对realmUser 的任何改变都会写入Realm,对user原始对象则没有效果。
关系
任意两个 RealmObject 可以相互关联。
public class Email extends RealmObject {
private String address;
private boolean active;
// ... setters and getters left out
}
public class Contact extends RealmObject {
private String name;
private Email email;
// ... setters and getters left out
}
- 多对一
你只需要简单地声明一个 Realm 模型类的属性即可:
public class Contact extends RealmObject {
private Email email;
// Other fields…
}
每个 Contact 对象都有 0 或 1 个 Email 对象。在 Realm 中,你可以任意在多个 Contact 对象中使用同一个 Email 对象。同理,这个例子也解释了怎样实现一对一关系。
设置一个类型为 RealmObject 的属性为空值(null)会清除该属性的引用,但并不会删除对应的 RealmObject。
- 多对多
你可以通过使用 RealmList 为一个对象关联0或多个其它对象。设想一个通讯录成员拥有多个 email:
public class Contact extends RealmObject {
public String name;
public RealmList<Email> emails;
}
public class Email extends RealmObject {
public String address;
public boolean active;
}
RealmList 是 Realm 模型对象的容器,其行为与 Java 的普通 List 近乎一样。同一个 Realm 模型对象可以存在于多个 RealmList 中。同一个 Realm 模型对象可以在同一个 RealmList 中存在多次。你可以使用 RealmList 来表现一对多和多对多的数据关系。
你可以创建对象,然后使用 RealmList.add() 来添加 Email 对象到 Contact。
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Contact contact = realm.createObject(Contact.class);
contact.name = "John Doe";
Email email1 = realm.createObject(Email.class);
email1.address = "john@example.com";
email1.active = true;
contact.emails.add(email1);
Email email2 = realm.createObject(Email.class);
email2.address = "jd@example.com";
email2.active = false;
contact.emails.add(email2);
}
});
设置一个类型为 RealmList 的属性为空值(null)会清空该列表,即列表长度变为 0。但并不会删除列表中的任何 RealmObject。RealmList 的获取器(getter)永不会返回 null。其返回对象永远是一个 RealmList 实例,但其长度有可能为0。