您的位置:首页 > 编程语言 > Java开发

Realm Java

2016-04-20 02:18 459 查看

Realm Java

当前这个翻译,主要是方便我自己查阅api,有很多地方写的比较晦涩或者没有翻译,敬请谅解


version 0.89.0

官方文档

Getting Started

Installation

工程的build.gradle中添加

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


引用module的build.gradle中添加

apply plugin: 'realm-android'


ProGuard

-keep class io.realm.annotations.RealmModule
-keep @io.realm.annotations.RealmModule class *
-keep class io.realm.internal.Keep
-keep @io.realm.internal.Keep class * { *; }
-dontwarn javax.**
-dontwarn io.realm.**


Models

Realm支持类型

boolean

byte

short

int

long

float

double

String

Date

byte[]

RealmObject

RealmList

Required fields and null values

必须属性和null值

在某些情况下,存在null值是不合理的.

@Required可以声明Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[], Date,不能为null.

当其他类型使用@Required,会导致编译错误.

Fields with primitive types and the RealmList type are required implicitly.(必须通过setter和getter,否则不会获取到正确数据)

RealmObject不需要默认值

Ignoring properties

@Ignore来声明Realm忽略的属性

Auto-Updating Objects

和ios一样

顾名思义当一个数据的内容改变时,它会自动更新该数据的所有实例化对象

realm.beginTransaction();
Dog myDog = realm.createObject(Dog.class);
myDog.setName("Fido");
myDog.setAge(1);
realm.commitTransaction();

Dog myPuppy = realm.where(Dog.class).equals("age", 1).findFirst();
realm.beginTransaction();
myPuppy.setAge(2);
realm.commitTransaction();

myDog.getAge(); // => 2


当数据变化,需要更新界面时,需要配合 [Realm notifications](#Realm notifications) 实现,后续会详细描述这功能

Indexed Properties

@Index 来修饰索引类型

索引支持类型

String

byte

short

int

long

boolean

Date

注意事项(IOS的,Android没有说)

Indexing a property will greatly speed up queries where the property is compared for equality (i.e. the = and IN operators), at the cost of slower insertions.


使用索引增加查询速度的代价是插入数据时速度会降低


Primary Keys

@PrimaryKey 用来修饰主键.@PrimaryKey可以修饰String,short,int,long.

@PrimaryKey只能修饰一个属性.

@PrimaryKey声明一个主键后,该属性会被隐性声明成@Index.

@PrimaryKey声明一个主键后,该属性会被隐性声明成@Required,不能为null.

声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。

一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。

当调用Realm.createObject(),它会返回一个有默认值的对象,当有主键时,主键会被设置默认值,这样可能会与已有对象冲突.所以最好使用copyToRealm()或者copyToRealmOrUpdate()替换

MyObject obj = new MyObject();
obj.setId(42);
obj.setName("Fish");
realm.beginTransaction();
// This will create a new one in Realm
// realm.copyToRealm(obj);
// This will update a existing one with the same id or create a new one instead
realm.copyToRealmOrUpdate(obj);
realm.commitTransaction();


Ignored Properties

重写 Object.ignoredProperties() 可以防止 Realm 存储数据模型的某个属性。Realm 将不会干涉这些属性的常规操作,它们将由成员变量(ivar)提供支持,并且您能够轻易重写它们的 setter 和 getter。

class Person: Object {
dynamic var tmpID = 0
var name: String { // read-only properties are automatically ignored
return "\(firstName) \(lastName)"
}
dynamic var firstName = ""
dynamic var lastName = ""

override static func ignoredProperties() -> [String] {
return ["tmpID"]
}
}


Limitations

Realm不支持final,transient和volatile修饰

Relationships

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
}


一对一(To-One Relationships)

public class Contact extends RealmObject {
private Email email;
// Other fields…
}


如果将email设为null,会断开2个实例间的关系,但是email实例没有被删除

一对多(To-Many Relationships)

class Person: Object {
public class Contact extends RealmObject {
private RealmList<Email> emails;
// Other fields…
}
}


Link queries

public class Person extends RealmObject {
private String id;
private String name;
private RealmList<Dog> dogs;
// getters and setters
}

public class Dog extends RealmObject {
private String id;
private String name;
private String color;
// getters and setters
}




// users => [U1,U2]
RealmResults<User> users = realm.where(User.class)
.equalTo("dogs.color", "Brown")
.findAll();


// r1 => [U1,U2]
RealmResults<User> r1 = realm.where(User.class)
.equalTo("dogs.name", "Fluffy")
.equalTo("dogs.color", "Brown")
.findAll();

// r2 => [U2]
RealmResults<User> r2 = realm.where(User.class)
.equalTo("dogs.name", "Fluffy")
.findAll()
.where()
.equalTo("dogs.color", "Brown")
.findAll();
.where()
.equalTo("dogs.color", "Yellow")
.findAll();


Writes

所有写入操作(添加,修改,删除)都必须依托一个write transaction.

由于write transaction会占用一定的资源,所以尽量精简write transaction的个数.当队列写入时,只需要一个就write transaction

在UI线程和后台线程同时开始写事务时,会导致ARN

写事务都是线程安全的.

// Obtain a Realm instance
Realm realm = Realm.getDefaultInstance();

realm.beginTransaction();

//... add or update objects here ...

realm.commitTransaction();


当开始写操作后,可以随时取消

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

//  ...

realm.cancelTransaction();


Creating Objects

由于RealmObjects必须依赖在Realm,所以其创建方式如下:

realm.beginTransaction();
User user = realm.createObject(User.class); // Create a new object
user.setName("John");
user.setEmail("john@corporation.com");
realm.commitTransaction();

//========other

User user = new User("John");
user.setEmail("john@corporation.com");

// Copy the object to Realm. Any further changes must happen on realmUser
realm.beginTransaction();
User realmUser = realm.copyToRealm(user);
realm.commitTransaction();


当使用realm.copyToRealm后,对原始数据修改,将不会被同步到Realm中.

Transaction blocks

会自动执行realm.beginTransaction(), realm.commitTransaction()

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");
}
});


Asynchronous Transactions

异步事务,主要是用于大量数据写入

realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
User user = bgRealm.createObject(User.class);
user.setName("John");
user.setEmail("john@corporation.com");
}
}, 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.
}
});


RealmAsyncTask transaction = realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
User user = bgRealm.createObject(User.class);
user.setName("John");
user.setEmail("john@corporation.com");
}
}, null);

public void onStop () {
if (transaction != null && !transaction.isCancelled()) {
transaction.cancel();
}
}


Queries

通过查询操作,Realm 将会返回包含 Object 集合的Results实例。Results 的表现和 List 十分相似。

所有的查询(包括查询和属性访问)在 Realm 中都是延迟加载的,只有当属性被访问时,才能够读取相应的数据。也就是说当没有使用数据前,进行多次排序或者过滤都是不需要额外cpu时间的

查询结构不是Copy对象,而是引用对象.所以在Write操作中修改查询数据,是直接修改数据库中的数据.

Realm使用Fluent interface,也就是流接口

基本查询语句

// Build the query looking at all users:
RealmQuery<User> query = realm.where(User.class);

// Add query conditions:
query.equalTo("name", "John");
query.or().equalTo("name", "Peter");

// Execute the query:
RealmResults<User> result1 = query.findAll();

// Or alternatively do the same all at once (the "Fluent interface"):
RealmResults<User> result2 = realm.where(User.class)
.equalTo("name", "John")
.or()
.equalTo("name", "Peter")
.findAll();


当查询内容为空时,RealmResults不会为null,它的size==0

Conditions

between, greaterThan(), lessThan(), greaterThanOrEqualTo() & lessThanOrEqualTo()

equalTo() & notEqualTo()

contains(), beginsWith() & endsWith()

Logical Operators

主要是or()和not()

not(),用来否定花括号中的条件

流接口中支持”花括号”,beginGroup() => ‘{’ , endGroup() => ‘}’

RealmResults<User> r = realm.where(User.class)
.greaterThan("age", 10)  //implicit AND
.beginGroup()
.equalTo("name", "Peter")
.or()
.contains("name", "Jo")
.endGroup()
.findAll();


Sorting

RealmResults<User> result = realm.where(User.class).findAll();
result.sort("age"); // Sort ascending
result.sort("age", Sort.DESCENDING);


Chaining Queries

RealmResults<User> teenagers = realm.where(User.class).between("age", 13, 20).findAll();
User firstJohn = teenagers.where().equalTo("name", "John").findFirst();

RealmResults<User> teensWithPups = realm.where(User.class).between("age", 13, 20).equalTo("dogs.age", 1).findAll();


Auto-Updating Results

结果会自动更新

RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.size(); // => 0

realm.beginTransaction();
Dog dog = realm.createObject(Dog.class);
dog.setName("Fido");
dog.setAge(1);
realm.commitTransaction();

puppies.size(); // => 1


Retrieving objects by type

realm.allObjects()

Aggregation

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();


Iterations

RealmResults<User> results = realm.where(User.class).findAll();
for (User u : results) {
// ... do something with the object ...
}

RealmResults<User> results = realm.where(User.class).findAll();
for (int i = 0; i < results.size(); i++) {
User u = results.get(i);
// ... do something with the object ...
}

// Find all dogs older than or equal to 2 and change their age to 1
RealmResults results = realm.where(Dog.class).greaterThanOrEqualTo("age", 2).findAll();
realm.beginTransaction();
for (int i = results.size() -1; i >=0; i--) {
results.get(i).setAge(1);
}
realm.commitTransaction();


Deletion

// obtain the results of a query
RealmResults<Dog> results = realm.where(Dog.class).findAll();

// All changes to data must happen in a transaction
realm.beginTransaction();

// remove single match
results.remove(0);
results.removeLast();

// remove a single object
Dog dog = results.get(5);
dog.removeFromRealm();

// Delete all matches
results.clear();

realm.commitTransaction()


Asynchronous Queries

private RealmChangeListener callback = new RealmChangeListener() {
@Override
public void onChange() { // called once the query complete and on every update
// use the result
}
};

public void onStart() {
RealmResults<User> result = realm.where(User.class).findAllAsync();
result.addChangeListener(callback);
}
public void onStop () {
result.removeChangeListener(callback); // remove a particular listener
// or
result.removeChangeListeners(); // remove all registered listeners
}


Check if the query has completed

当RealmResults有数据绑定后,result.isLoaded(),会一直返回true

RealmResults<User> result = realm.where(User.class).findAllAsync();
if (result.isLoaded()) {
// Results are now available
}


Force load an asynchronous query

强制执行异步查询,会阻塞当前线程.

RealmResults<User> result = realm.where(User.class).findAllAsync();
result.load() // be careful, this will block the current thread until it returns


Non-Looper threads

异步查询只能使用在有Looper的线程上,否则会抛出异常.

Realms

Realm Configuration

Realm.setDefaultConfiguration()直接设置默认配置

Realm默认会将文件存储在Context.getFilesDir(),文件路径是无法修改的

// The RealmConfiguration is created using the builder pattern.
// The realm file will be located in Context.getFilesDir() with name "myrealm.realm"
RealmConfiguration config = new RealmConfiguration.Builder(context)
.name("myrealm.realm")
.encryptionKey(getKey())
.schemaVersion(42)
.setModules(new MySchemaModule())
.migration(new MyMigration())
.build();
// Use the config
Realm realm = Realm.getInstance(config);

RealmConfiguration myConfig = new RealmConfiguration.Builder(context)
.name("myrealm.realm").
.schemaVersion(2)
.setModules(new MyCustomSchema())
.build();

RealmConfiguration otherConfig = new RealmConfiguration.Builder(context)
.name("otherrealm.realm")
.schemaVersion(5)
.setModules(new MyOtherSchema())
.build();

Realm myRealm = Realm.getInstance(myConfig);
Realm otherRealm = Realm.getInstance(otherConfig);


The Default RealmConfiguration

public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// The realm file will be located in Context.getFilesDir() with name "default.realm"
RealmConfiguration config = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(config);
}
}

public class MyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Realm realm = Realm.getDefaultInstance();
// ... Do something ...
realm.close();
}
}


In-Memory Realms

内存中的Realms,没有保存在磁盘上.

优点:可以快速的访问数据,而不需要考虑数据持久化的性能开销.内存Realms只会在temp路径里存放几个文件,用来进行线程间数据同步,不会将Realms中任何数据写入磁盘中

Dynamic Realms

不需要建立基于RealmObject类,只需要通过字符串来操作.

主要优势是灵活

RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
DynamicRealm realm = DynamicRealm.getInstance(realmConfig);

// In a DynamicRealm all objects are DynamicRealmObjects
DynamicRealmObject person = realm.createObject("Person");

// All fields are accesssed using strings
String name = person.getString("name");
int age = person.getInt("age");

// An underlying schema still exists, so accessing a field that does not exist
// will throw an exception
person.getString("I don't exist");

// Queries stil work normally
RealmResults<DynamicRealmObject> persons = realm.where("Person")
.equalTo("name", "John")
.findAll();


Closing Realm instances

protected Void doInBackground(Void... params) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
// ... Use the Realm instance
} finally {
if (realm != null) {
realm.close();
}
}

return null;
}


在有Looper的线程中,应该如下使用:

public class MyThread extends Thread {

private Realm realm;

public void run() {
Looper.prepare();
try {
realm = Realm.getDefaultInstance();

//... Setup the handlers using the Realm instance
Lopper.loop();
} finally {
if (realm != null) {
realm.close();
}
}
}
}


在minSdkVersion >= 19时,可以如下使用:

try (Realm realm = Realm.getDefaultInstance()) {
// No need to close the Realm instance manually
}


Auto-Refresh

在有Looper的线程上(包括UI线程)会自动更新.

可以使用isAutoRefresh()来知晓是否当前Realm是否支持自动刷新

Threading

这一章主要是讲多线程开发,大量写入事务最好是放在其他线程中,以防止UI线程被阻塞

只在一个线程中处理所有事情,不需要担心并发和多线程.(然并卵的话)

Realm在多线程处理上不需要使用线程锁,只需要注意写入操作需要在Write事件中.

Realm Threading Example

// in a Fragment or Activity, etc
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// ... boilerplate omitted for brevity
realm = Realm.getDefaultInstance();
// get all the customers
RealmResults<Customer> customers = realm.where(Customer.class).findAllAsync();
// ... build a list adapter and set it to the ListView/RecyclerView/etc

// set up a Realm change listener
changeListener = new RealmChangeListener() {
@Override
public void onChange() {
// This is called anytime the Realm database changes on any thread.
// Please note, change listeners only work on Looper threads.
// For non-looper threads, you manually have to use Realm.refresh() instead.
listAdapter.notifyDataSetChanged(); // Update the UI
}
};
// Tell Realm to notify our listener when the customers results
// have changed (items added, removed, updated, anything of the sort).
customers.addChangeListener(changeListener);
}

// In a background service, in another thread
public class PollingService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Realm realm = Realm.getDefaultInstance();
// go do some network calls/etc and get some data and stuff it into a 'json' var
String json = customerApi.getCustomers();
realm.beginTransaction();
realm.createObjectFromJson(Customer.class, json); // Save a bunch of new Customer objects
realm.commitTransaction();
// At this point, the data in the UI thread is already up to date.
// ...
}
// ...
}


Using a Realm across Threads

在UI线程或者其他添加Runloop的线程上,Realm都会自动更新其他线程Runloop的操作结果.([b]这里是说其他线程有更新,UI线程或Runloop线程都不会更新数据)
[/b]

在其他类型的线程上操作,都是基于Snapshots.

UI线程或者其他添加Runloop的线程上,数据都会自动刷新,除非将Realm.autorefresh设置为NO

其他类型的线程,都是以最后一次修改成功的Realm为snapshot,除非是手动refresh

Realm, RealmObject or RealmResults不能进行多线程传递.

最好使用asynchronous query和asynchronous transaction

```

##Schemas
可以让Realm只包含特定类型.


// Create the module

@RealmModule(classes = { Person.class, Dog.class })

public class MyModule {

}

// Set the module in the RealmConfiguration to allow only classes defined by the module.

RealmConfiguration config = new RealmConfiguration.Builder(context)

.setModules(new MyModule())

.build();

// It is possible to combine multiple modules to one schema.

RealmConfiguration config = new RealmConfiguration.Builder(context)

.setModules(new MyModule(), new MyOtherModule())

.build();

###Sharing schemas

```基于Library开发时,需要注意Realm必须expose并且Realm必须设置对应的schema.
如果不设置schema,当你使用它们时,会抛出异常.

<div class="se-preview-section-delimiter"></div>


// Library must create a module and set library = true. This will prevent the default
// module from being created.
// allClasses = true can be used instead of listing all classes in the library.
@RealmModule(library = true, allClasses = true)
public class MyLibraryModule {
}

// Library projects are therefore required to explicitly set their own module.
RealmConfiguration libraryConfig = new RealmConfiguration.Builder(context)
.name("library.realm")
.setModules(new MyLibraryModule())
.build();

// Apps can add the library RealmModule to their own schema.
RealmConfiguration config = new RealmConfiguration.Builder(context)
.name("app.realm")
.setModules(Realm.getDefaultModule(), new MyLibraryModule())
.build();

<div class="se-preview-section-delimiter"></div>


JSON

支持导入类型String,JSONObject,JSONArray和InputStream.

// A RealmObject that represents a city
public class City extends RealmObject {
private String city;
private int id;
// getters and setters left out ...
}

// Insert from a string
realm.beginTransaction();
realm.createObjectFromJson(City.class, "{ city: \"Copenhagen\", id: 1 }");
realm.commitTransaction();

// Insert from a JSONObject
realm.beginTransaction();
realm.createObjectFromJson(City.class, jsonObject);
realm.commitTransaction();

// Insert from a JSONArray
realm.beginTransaction();
realm.createObjectFromJson(City.class, jsonArray);
realm.commitTransaction();

// Insert multiple items using a InputStream
InputStream is = new FileInputStream(new File("path_to_file"));
realm.beginTransaction();
try {
realm.createAllFromJson(City.class, is);
realm.commitTransaction();
} catch (IOException e) {
realm.cancelTransaction();
}

<div class="se-preview-section-delimiter"></div>


注意事项

使用JSON创建对象属性为null值

没有用@Required修饰的会被赋值null

用@Required的会抛出异常

使用JSON更新对象属性为null值

没有用@Required修饰的会被赋值null

用@Required的会抛出异常

JSON没有属性对应的字段

Leave the value unchanged for both required and not-required fields.(意思好像是,不会改变其原值,没有具体说是创建还是更新)

Notifications

修改Listeners只会在有Looper的线程(包含UI线程)中回调,其他线程只能使用Realm.refresh()来刷新

在多线程中修改数据,有Looper的线程(包含UI线程)都会收到Listeners的回调

public class MyActivity extends Activity {
private Realm realm;
// A reference to RealmChangeListener needs to be held to avoid being
// removed by the garbage collector.
private RealmChangeListener realmListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
reamlListener = new RealmChangeListener() {
@Override
public void onChange() {
// ... do something with the updates (UI, etc.) ...
}};
realm.addChangeListener(realmListener);
}

@Override
protected void onDestroy() {
super.onDestroy();
// Remove the listener.
realm.removeChangeListener(realmListener);
// Close the realm instance.
realm.close();
}
}

<div class="se-preview-section-delimiter"></div>


关闭所有Listeners

realm.removeAllChangeListeners();

<div class="se-preview-section-delimiter"></div>


RealmObject和RealmResults都可以添加Listeners

public class MyActivity extends Activity {
private Realm realm;
private RealmChangeListener puppiesListener;
private RealmChangeListener dogListener;

private RealmResults<Dog> puppies;
private Dog dog;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
puppiesListener = new RealmChangeListener() {
@Override
public void onChange() {
// ... do something with the updated puppies instance
}};

// Find all the puppies
puppies = realm.where(Dog.class).lessThanOrEqualTo("age", 2).findAll();
puppies.addChangeListener(puppiesListener);

dogListener = new RealmChangeListener() {
@Override
public void onChange() {
// ... do something with the updated Dog instance
}};

dog = realm.where(Dog.class).equals("name", "Fido").findFirst();
dog.addChangeListener(dogListener);
}

@Override
protected void onDestroy() {
super.onDestroy();
// Remove the listeners
puppies.removeChangeListener(puppiesListener);
dog.removeChangeListener(dogListener);
// Close the realm instance.
realm.close();
}
}

<div class="se-preview-section-delimiter"></div>


给类添加Listeners

Person person = realm.where(Person.class).findFirst();
person.getDogs(); // => 2 - Assume there are 2 dogs in the list
person.addChangeListener(new RealmChangeListener() {
@Override
public void onChange() {
// React to the change in the Person instance.
// This will also get called when any referenced dogs are updated.
}
});
Dog dog = person.getDogs().get(0);
realm.beginTransaction();
dog.setAge(5);
realm.commitTransaction();
// Person change listener is called on the next iteration of the run loop because
// a referenced dog object changed.

<div class="se-preview-section-delimiter"></div>


Migrations

数据迁移,版本迭代时,数据库常用

为什么要进行数据库迁移(借用Swift)

class Person: Object {
dynamic var firstName = ""
dynamic var lastName = ""
dynamic var age = 0
}

<div class="se-preview-section-delimiter"></div>


在某个版本更新中,变成了下边这样

class Person: Object {
dynamic var fullName = ""
dynamic var age = 0
}

<div class="se-preview-section-delimiter"></div>


那么就需要用到数据迁移了.

Linear Migrations

需要考虑跨版本的数据库迁移,例如v0直接升级到v3版本,而不是只考虑v2升级到v3.

// Example migration adding a new class
RealmMigration migration = new RealmMigration() {
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {

// During a migration, a DynamicRealm is exposed. A DynamicRealm is an untyped variant of a normal Realm, but
// with the same object creation and query capabilities.
// A DynamicRealm uses Strings instead of Class references because the Classes might not even exist or have been
// renamed.

// Access the Realm schema in order to create, modify or delete classes and their fields.
RealmSchema schema = realm.getSchema();

/************************************************
// Version 0
class Person
@Required
String firstName;
@Required
String lastName;
int    age;

// Version 1
class Person
@Required
String fullName;            // combine firstName and lastName into single field.
int age;
************************************************/
// Migrate from version 0 to version 1
if (oldVersion == 0) {
RealmObjectSchema personSchema = schema.get("Person");

// Combine 'firstName' and 'lastName' in a new field called 'fullName'
personSchema
.addField("fullName", String.class, FieldAttribute.REQUIRED)
.transform(new RealmObjectSchema.Function() {
@Override
public void apply(DynamicRealmObject obj) {
obj.set("fullName", obj.getString("firstName") + " " + obj.getString("lastName"));
}
})
.removeField("firstName")
.removeField("lastName");
oldVersion++;
}

/************************************************
// Version 2
class Pet                   // add a new model class
@Required
String name;
@Required
String type;

class Person
@Required
String fullName;
int age;
RealmList<Pet> pets;    // add an array property

************************************************/
// Migrate from version 1 to version 2
if (oldVersion == 1) {

// Create a new class
RealmObjectSchema petSchema = schema.create("Pet")
.addField("name", String.class, FieldAttribute.REQUIRED)
.addField("type", String.class, FieldAttribute.REQUIRED);

// Add a new field to an old class and populate it with initial data
schema.get("Person")
.addRealmListField("pets", petSchema)
.transform(new RealmObjectSchema.Function() {
@Override
public void apply(DynamicRealmObject obj) {
if (obj.getString("fullName").equals("JP McDonald")) {
DynamicRealmObject pet = realm.createObject("Pet");
pet.setString("name", "Jimbo");
pet.setString("type", "dog");
obj.getList("pets").add(pet);
}
}
});
oldVersion++;
}

/************************************************
// Version 3
class Pet
@Required
String name;
int type;               // type becomes int

class Person
String fullName;        // fullName is nullable now
RealmList<Pet> pets;    // age and pets re-ordered (no action needed)
int age;
************************************************/
// Migrate from version 2 to version 3
if (oldVersion == 2) {
RealmObjectSchema personSchema = schema.get("Person");
personSchema.setNullable("fullName", true); // fullName is nullable now.

// Change type from String to int
schema.get("Pet")
.addField("type_tmp", int.class)
.transform(new RealmObjectSchema.Function() {
@Override
public void apply(DynamicRealmObject obj) {
String oldType = obj.getString("type");
if (oldType.equals("dog")) {
obj.setLong("type_tmp", 1);
} else if (oldType.equals("cat")) {
obj.setInt("type_tmp", 2);
} else if (oldType.equals("hamster")) {
obj.setInt("type_tmp", 3);
}
}
})
.removeField("type")
.renameField("type_tmp", "type");
oldVersion++;
}
}

<div class="se-preview-section-delimiter"></div>


Encryption

Realm的加密方式为:key为64字节,AES-256

加密过的 Realm 只会带来很少的额外资源占用(通常最多只会比平常慢10%)

注:如果数据库加密后,由于不知道加密方式,即使有原始key,也无法获取解密key,所以无法用Realm Browser查看.

注:如果数据库加密,每次获取Realm实例时,必须使用encryptionKey.

byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
RealmConfiguration config = new RealmConfiguration.Builder(context)
.encryptionKey(key)
.build();

Realm realm = Realm.getInstance(config);

<div class="se-preview-section-delimiter"></div>


Other Libraries

GSON

GSON is a library created by Google for deserializing and serializing JSON. When using Realm with GSON 2.3.1 (latest version), you have to specify an ExclusionStrategy.

// Using the User class
public class User extends RealmObject {
private String name;
private String email;
// getters and setters left out ...
}

Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class);
}

@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.create();

String json = "{ name : 'John', email : 'john@corporation.com' }";
User user = gson.fromJson(json, User.class);

<div class="se-preview-section-delimiter"></div>


Retrofit

Retrofit 1.*

uses GSON internally, it also needs a properly configured GsonConverter if you want to deserialize network JSON data to RealmObjects.

Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class);
}

@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.create();

// Configure Retrofit to use the proper GSON converter
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.setConverter(new GsonConverter(gson))
.build();

GitHubService service = restAdapter.create(GitHubService.class);

<div class="se-preview-section-delimiter"></div>


Retrofit 2.*

With Retrofit 2, GSON is no longer used by default, but can be used via its converter module.

dependencies {
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3'

<div class="se-preview-section-delimiter"></div>


Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class);
}

@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.create();

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create(gson)
.build();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: