数据处理安全

Realm使用文档

2017-03-01  本文已影响69人  Will222

前提

Android Studio >= 1.5.1
A recent version of the Android SDK.
JDK version >=7.
支持Api>9的Android版本

环境配置

Step 1: Add the following class path dependency to the project level build.gradle file.

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

The project level build.gradle
file is located here:

Project level build.gradle fileProject level build.gradle file
Step 2: Apply the realm-android
plugin to the top of application level build.gradle
file.
apply plugin: 'realm-android'

The application level build.gradle

is located here: Application level build.gradle fileApplication level build.gradle file

Models

Realm model classes are created by extending the RealmObject
base class.

public class User extends RealmObject { 
   private String name; 
   private int age;
   @Ignore 
   private int sessionId; // Standard getters & setters generated by your IDE…          
   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 int getSessionId() { 
       return sessionId; 
   } 
   public void setSessionId(int sessionId) { 
       this.sessionId = sessionId; 
   }
}

一个Realm的类支持 public,protected 和 private和自定义方法

public class User extends RealmObject { 
    public String name; 
    public boolean hasLongName() { 
        return name.length() > 7; 
    } 
    @Override 
    public boolean equals(Object o) {
     // Custom equals comparison 
    }
  }

Relationships

任意两个 RealmObjects 可以一起使用

  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
  }

多对一
简单地声明一个属性的类型是 RealmObject的子类 :

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

每一个contact对象只有0或者1email的实例,如果设置RealmObject的字段为空,会把两个表的关联取消,但不会删除相应表的数据

多对多

你可以建立几个对象和一个对象的关系通过使用RealmList<T> 的字段。
如下:

public class Contact extends RealmObject { 
   public String name; 
   public RealmList<Email> emails;
}
public class Email extends RealmObject { 
    public String address; 
    public boolean active;
}

RealmList 基本上是RealmObject的容器,RealmList就像Java中的List一样。可以无限制的使用同一个对象两次或多次在不同的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会清空list,list会变为空(长度为0),但对象并不会被删除。
RealmList的getter方法永远都不会返回为空,但返回的对象会一直是长度为0的list。

链式查询

可以查询链接或关系。
Consider the model below:

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
}

每个Person的对象有不同dog的关系像如下表中:


Table DiagramTable Diagram

我们查找某个Person通过链式查询
// persons => [U1,U2]

RealmResults<Person> persons = realm.where(Person.class) .equalTo("dogs.color", "Brown") .findAll();

这个查询会获得所有Persons的dogs的color属性为Brown的

或者通过persons的对象查询中含有的Dog

persons.get(0).getDogs(); // => [A,B]
persons.get(1).getDogs(); // => [B,C,D]

这可以通过以下两个查询进一步检查。

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

// r2 => [U1,U2]
RealmResults<Person> r2 = r1.where().equalTo("dogs.color", "Brown") .findAll();

Notice how the first query returned both Person
objects because the condition matched both persons. Each Person
in the query result contains a list of Dog
objects - all of their dog objects (even ones that do not fulfill the original query condition). Remember, we’re searching for people who have particular kinds of dogs (names and colors), not the actual dogs themselves. Therefore, the second query will be evaluated against the first Person
query result (r1
) and each of the Person
s dogs. The second query also matches both persons as well, but this time it’s because of the color of the dog.
Let’s dig a little deeper to help solidify this concept. Please review the following example:

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

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

第一句的查询为,查找所有Person中dogs的“name”为“Fluffy”并且dogs的"color"为”Brown“的Person数据,第二句查询为查询dogs的“name”为“Fluffy”,dogs的color为"Brown"和dogs的color为"Yellow"的Person集合

写(Writes)

所有写的操作(添加,修改,和移除对象)必须使用事务(transaction)。一个transaction可以被提交或者被取消,如果提交了,所有的改变都会更新到磁盘上。如果取消transaction,所有的改变都会取消。transaction的使用是线程安全的。

// Obtain a Realm instance
Realm realm = Realm.getDefaultInstance()
realm.beginTransaction();//... add or update objects here ...
realm.commitTransaction();

当你开启了事务后,你可以使用cancelTransaction来丢弃之前的更改

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

请记住transactions是堵塞线程的,这会很容易导致ANR的错误,如果你在UI线程中和子线程中都创建transction了。为了避免ANR,请使用异步的transaction,当在UI线程中创建transanction时。

创建对象

创建对象请使用以下方法:

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

或者使用realm.copyToRealm():

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

事务堵塞(Transaction blocks)

除了 realm.beginTransaction(), realm.commitTransaction(), and realm.cancelTransaction(),你还可以使用realm.executeTransaction(),这个方法会自动调用begin/commit,然后发生错误时,会调用cancel

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)

As transactions are blocked by other transactions it can be an advantage to do all writes on a background thread in order to avoid blocking the UI thread. By using an asynchronous transaction, Realm will run that transaction on a background thread and report back when the transaction is done.

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

OnSuccess和OnError的回调是可选的,但是必须使用在创建了Looper的线程中

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

异步事务由RealmAsyncTask的对象表示,在退出Activity/Fragment 时,而transaction操作未完成,就可以使用这个对象进行取消事务操作,避免页面崩溃

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

查询

Using the User class -

public class User extends RealmObject { 
  @PrimaryKey 
  private String name; 
  private int age; 
  @Ignore 
  private int sessionId; // Standard getters & setters generated by your IDE… 
  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 int getSessionId() {
   return sessionId; 
  }
  public void setSessionId(int sessionId) {
   this.sessionId = sessionId; 
  }
}  

为了查找所有的users中name为“John”或“Peter”:

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

// 或者链式调用
RealmResults<User> result2 = realm.where(User.class) 
                                  .equalTo("name", "John")    
                                  .or() 
                                  .equalTo("name", "Peter")
                                  .findAll();

这会返回一个RealmResults的对象,这包含了姓名为John或Peter的user。

RealmResults继承自AbstractList,跟普通的List有相同的特性,例如,你可以通过索引来查询某个对象,如果没有匹配的,RealmResults不会为null,但size()为0。
如果你想进行更改或者删除对象在RealmResults中,你必须使用transaction。

条件查询
以下的条件操作符是支持的:

 between(), greaterThan(), lessThan(), greaterThanOrEqualTo() & lessThanOrEqualTo()
 equalTo() & notEqualTo()
 contains(), beginsWith()& endsWith()
 isNull() & isNotNull()
 isEmpty() & isNotEmpty()

不是所有的数据的类型都支持以上条件操作,请查询相关RealmQueryAPI.

逻辑运算符

每个条件符的都是默认为一同使用,如果要使用or的逻辑,必须显示调用or()操作符

Using the User class -

public class User extends RealmObject { 
    @PrimaryKey 
    private String name; 
    private int age; 
    @Ignore 
    private int sessionId; // Standard getters & setters generated by your IDE… 
    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 int getSessionId() {
         return sessionId;
    } 
    public void setSessionId(int sessionId) {
         this.sessionId = sessionId; 
    }
}

You can also group conditions with “parentheses” to specify order of evaluation: beginGroup()
is your “left parenthesis” and endGroup()
your “right parenthesis”:

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

Furthermore, it is possible to negate a condition with not(). The not() operator can be used with beginGroup()/endGroup() to negate sub-conditions only.

排序

Once you have done your query, you can sort the results like this:

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

链式查询

你可以高效地使用链式操作去查询你的数据:

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

You can also perform chained queries on child objects as well. Assume the above Person object has a list of Dog objects.

public class Dog extends RealmObject { 
    private int age; // getters & setters ...
}
public class Person extends RealmObject { 
    private int age; 
    private RealmList<Dog> dogs; // getters & setters ...
}

You can query for all people who are between the age of 13 and 20 who have at least one dog which is 1 year old:

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

Note that query chains are built on RealmResults, not RealmQuery. If you add more conditions to an existing RealmQuery, then you are modifying the query, not the chains. Read more about link queries.

自动更新 结果(Auto-Updating Results)

RealmResults 是会自己进行更新的,这意味着不需要重新进行查询操作,修改对象的操作会直接影响到之前查询的结果

  final RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
  puppies.size(); // => 0
  realm.executeTransaction(new Realm.Transaction() { 
    @Override  
    public void execute(Realm realm) { 
       Dog dog = realm.createObject(Dog.class);
       dog.setName("Fido");
       dog.setAge(1); 
    }
  });
  puppies.addChangeListener(new RealmChangeListener() { 
    @Override 
    public void onChange(RealmResults<Dog> results) { 
    // results and puppies point are both up to date 
     results.size(); // => 1
     puppies.size(); // => 1   
    }
  });

这适用于所有RealmResults:所有对象、过滤和链接。
这个特性不单止可以使Realm快和高效率,也可以使代码更简洁更有效。
你可以去 Realm notifications了解什么时候你的UI数据会进行更新。

聚合(Aggregation)[]

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

迭代(Iterations)

遍历RealmResults里面的所有对象
你可以利用Iterable的特性:

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 ...
}

RealmResults会自动地进行更新,但是还会有很小的几率在获取对象后,不能匹配查询或者删除后的结果

final RealmResults<User> users = getUsers();
realm.executeTransaction(new Realm.Transaction() { 
@Override 
  public void execute(Realm realm) { 
    users.get(0).deleteFromRealm(); // 非指向性删除对象
  }
});
for (User user : users) { 
  showUser(user); // 会导致crash
}

不要使用 deleteFromRealm()在RealmResults:

final RealmResults<User> users = getUsers();
realm.executeTransaction(new Realm.Transaction() {
  @Override 
  public void execute(Realm realm) { 
    users.deleteFromRealm(0); // Delete and remove object directly 
  }
});
for (User user : users) { 
  showUser(user); // Deleted user will not be shown
}

删除(Deletion)

你可以在进行查询后进行删除:

// obtain the results of a query 
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(); 
  }
});

异步地查询(Asynchronous Queries)

查询可以使用子线程

大部分地Realm的同步查询操作是非常快的,即使是在UI线程中。但是对于很复杂的查询或者很大数据的查询,在子线程中进行查询会更好

Example: finding users with name “John” or “Peter”

Create the query
RealmResults<User> result = realm.where(User.class) 
                                 .equalTo("name", "John")
                                 .or() 
                                 .equalTo("name", "Peter") 
                                 .findAllAsync();

这个查询会在子线程中进行,并且查询完毕后,会立刻返回RealmResults的对象
如果你想响应查询完成后的对象,你可以注册一个RealmChangeListener

注册一个回调

private RealmChangeListener callback = new RealmChangeListener() {
    @Override
    public void onChange(RealmResults<User> results) {
        // called once the query complete and on every update
    }
 };

public void onStart() {
    RealmResults<User> result = realm.where(User.class).findAllAsync();
    result.addChangeListener(callback);
}

请记得要注销任何一个listener在Acitvity或Fragment中,以避免内存泄漏

public void onStop () {
    result.removeChangeListener(callback); // remove a particular listener
    // or
    result.removeChangeListeners(); // remove all registered listeners
}

检测查询是否完成

 RealmResults<User> result = realm.where(User.class).findAllAsync();
    if (result.isLoaded()) {
   // Results are now available
 }
同步地调用isLoaded()会一直返回true

使用异步检测查询(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

Realms

请在Application的子类中进行初始化:

public class MyApplication extends Application { 
  @Override 
  public void onCreate() { 
     super.onCreate(); 
     Realm.init(this);
   }
}

Realm的配置(Configuring a Realm)

最简单地配置:

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

一般我们会进行这样的配置:

// 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()
  .name("myrealm.realm")
  .encryptionKey(getKey())
  .schemaVersion(42)
  .modules(new MySchemaModule())
  .migration(new MyMigration())
  .build();
// Use the config
Realm realm = Realm.getInstance(config);

你也可以拥有不同的RealmConfigurations。以这种方式,你可以独立地控制每个Realm版本,schema和位置

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

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

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

通过调用Realm.getPath()可以获得Realm的绝对路径

默认地配置(The Default RealmConfiguration)

RealmConfiguration会被设置为默认的configuration,设置一个默认的configuration在你自己的Application中

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

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

In-Memory Realm

定义一个不需要持久化的Realm:

RealmConfiguration myConfig = new RealmConfiguration.Builder().name("myrealm.realm") 
                                                              .inMemory() 
                                                              .build();

这会创建一个保存在内存中而不保存在磁盘中的Realm对象,In-memory Realms会使用磁盘内存如果内存过低时,但Realm关闭时,会删除所有数据

数据库迁移(Migrations)

如果数据库要进行升级,
This can be avoided by setting the schema version and migration code in RealmConfiguration
.

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

使用自定义的Migration,可以方便进行旧版本数据的操作,如下:

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

     // DynamicRealm exposes an editable schema
     RealmSchema schema = realm.getSchema();

     // Migrate to version 1: Add a new class.
     // Example:
     // public Person extends RealmObject {
     //     private String name;
     //     private int age;
     //     // getters and setters left out for brevity
     // }
     if (oldVersion == 0) {
        schema.create("Person")
            .addField("name", String.class)
            .addField("age", int.class);
        oldVersion++;
     }

     // Migrate to version 2: Add a primary key + object references
     // Example:
     // public Person extends RealmObject {
     //     private String name;
     //     @PrimaryKey
     //     private int age;
     //     private Dog favoriteDog;
     //     private RealmList<Dog> dogs;
     //     // getters and setters left out for brevity
     // }
     if (oldVersion == 1) {
        schema.get("Person")
            .addField("id", long.class, FieldAttribute.PRIMARY_KEY)
            .addRealmObjectField("favoriteDog", schema.get("Dog"))
            .addRealmListField("dogs", schema.get("Dog"));
         oldVersion++;
     }
  }
}

如果是测试阶段,使用以下的配置进行测试:

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

author lxw
date 2017.2.15

上一篇下一篇

猜你喜欢

热点阅读