<Android开源库> Realm For Android~ Queries(译文)

来源:互联网 发布:a星寻路算法介绍 编辑:程序博客网 时间:2024/05/01 12:14

查询

Realm 中的所有读取(包括查询)操作都是延迟执行的,且数据绝不会被拷贝。

Realm 的查询引擎使用 Fluent interface 来构造多条件查询。
使用 User 类 -

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

比如查找所有叫做 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();// 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();

这个查询将返回一个包含名为John或者Peter的人的RealmResults实例。在findAll()方法被调用的时候,查询操作就执行了。findAll()是findAll家族的一员,例如findAllSorted()将返回一个排序后的结果集;findAllAsync()将在后台线程中执行异步查询。具体详情请阅API指南。

这些对象并非拷贝,也就是说你得到的是一个匹配对象引用的列表,你对匹配对象所有的操作都是直接施加于它的原始对象。RealmResults 继承自 Java 的 AbstractList,行为类似。例如你可以通过 index 来访问其中的某个对象。
当查询没有任何匹配时,返回的 RealmResults 对象将不会为 null,取而代之的是它的 size() 方法将返回 0。

修改或删除 RealmResults 中任何一个对象都必须在写入事务中完成。

你也可以查询关系.


查询条件

使用where()方法构建一个由RealmQuery 对象代表的查询。一旦构建了RealmQuery ,你可以使用各种各样的谓词来过滤查询得到的数据。大多数谓词意义从字面上都能看出来。

谓词的第一个参数是字段名。 如果字段的类型不支持谓词,则抛出异常。 有关详细信息,请参阅RealmQuery的API参考。

对于所有数据类型,你有以下谓词:

  • equalTo()
  • notEqualTo()

要将字段与值列表匹配,请使用in()。 例如,要查找名称“Jill”,“William”或“Trillian”,您可以在(“name”,new String [] {“Jill”,“William”,“Trillian”}中使用。 in()谓词适用于字符串,二进制数据和数字字段。

数字数据类型(包括日期)允许这些附加谓词:

  • between() (两个端点会被包含在内, 即,有界间隔)
  • greaterThan()
  • lessThan()
  • greaterThanOrEqualTo()
  • lessThanOrEqualTo()

字符串字段允许如下附加谓词:

  • contains()
  • beginsWith()
  • endsWith()
  • like()

所有四个字符串谓词都有可选的第三个参数,用于控制大小写区分。 将其设置为Case.INSENSITIVE以在匹配时忽略大小写,或者将Case.SENSITIVE设置为执行区分大小写匹配。 默认值为Case.SENSITIVE。

谓词like()执行glob样式通配符匹配。 匹配模式由字符和一个或多个通配符组成:

  • * 匹配0个或多个Unicode字符
  • ?匹配1个Unicode字符

例如,考虑一个有四个对象的Realm,名为name的字段,其值有William,Bill,Jill和Trillian。 类似于(“name”,“?ill *”)的谓词将匹配前三个对象,类似(“name”,“* ia?”)将匹配第一个和最后一个对象。

二进制数据,字符串和RealmObject(RealmList)的列表可以是空的,即具有零的长度。 如下是检查是否为空的谓词:

  • isEmpty()
  • isNotEmpty()

如果不是必填字段,则值可以为null(记住,RealmObjects字段不是必填,值可以为null)。 用于匹配空值的两个有用谓词是:

  • isNull()
  • isNotNull()

逻辑运算符

每个查询条件都会被被隐式地被逻辑和(&)组合在一起,而逻辑或(or)需要显式地去执行 or()。

使用 User 类 -

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

你也可以将查询条件组合在一起,使用 beginGroup()(相当于左括号)和 endGroup()(相当于右括号):

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

此外,也可以用 not() 否定一个条件。该 not() 运算符可以与 beginGroup()/endGroup() 一起使用来否定子条件。假如说你想查询名字不是“Peter”或者“Jo.”的User。查询方式如下:

RealmResult<User> r = realm.where(User.class)                           .not()                           .beginGroup()                                .equalTo("name", "Peter")                                .or()                                .contains("name", "Jo")                            .endGroup()                            .findAll();

当然,对于这种特定的查询,使用in()更为简洁:

RealmResult<User> r = realm.where(User.class)                   .not()                   .in("name", new String[]{"Peter", "Jo"})                    finalAll();

排序

当你执行完查询获得结果后,可以对它进行排序:

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

排序默认为升序; 使用Sort.DESCENDING作为第二个可选参数可以修改为降序。 可以同时使用多个字段进行排序。


唯一值

使用distinct()谓词仅返回唯一值。 例如,要了解你的Realm中有多少不同的名字:

RealmResults<Person> unique = realm.where(Person.class).distinct("name");

只支持整数和字符串字段; 如果你尝试使用distinct()不支持的字段类型,将抛出异常。 与排序一样,你可以指定多个字段。


链式查询

因为查询结果并不会被复制,且在查询提交时并不会被执行,你可以链式串起查询并逐步进行分类筛选:

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

你也可以在子对象上使用链式查询。假设以上 Person 对象包含一个 Dog 对象列表:

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

你可以查询找出所有年龄在 13 和 20 之间的 Person 并且他至少拥有一个 1 岁的 Dog:

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

请注意,查询链最终是建立在 RealmResults 上而非 RealmQuery。如果你在某存在的 RealmQuery 上添加更多的查询条件,那么你在修改查询本身,而非查询链。请参考关联查询。


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

RealmResults 是对其所包含数据的实时、自动更新视图,这意味着它永远不需要被重新查询获取。数据对象的改变会在下一次 Looper 事件中被反映到相应的查询结果。

final RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();puppies.size(); // => 0realm.executeTransaction(new Realm.Transaction() {    @Override    void public 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 有效 —— 无论是否有过滤条件、是否是链式查询。

RealmResults 的这个特性不仅使得 Realm 快速高效,而且让你的代码更简洁。举例来说,假设你的 Activity 或者 Fragment 依赖于某个查询结果,你可以将相应的 Realm 对象或者 RealmResults 保存为一个属性,你不需要在每次访问时确定其是否被更新 —— Realm 会保证这些。

你可以通过订阅 Realm notifications 来得知 Realm 数据更新了,进而刷新 UI 而不必重新查询获得 RealmResults。

因为查询结果的自动更新特性,请不要依赖于固定的索引(indices)、不变的条目数。


按类型检索对象

从 Realm 中检索对象的最基本方法是 realm.where(Foo.class).findAll(),它返回了包含被查询模型类的所有对象的 RealmResults。

另外还有提供排序功能的 findAll()。参见 realm.where(Foo.class).findAllSorted() 了解详情。


聚合

RealmResult 自带一些聚合方法:

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

迭代

可以这样遍历 RealmResults:

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

或者使用 for 循环:

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 的自动更新会通过 looper 事件触发,但在事件到来之前,某些元素有可能不再满足查询条件或者其已被删除。

final RealmResults<User> users = getUsers();realm.executeTransaction(new Realm.Transaction() {    @Override    public void execute(Realm realm) {        users.get(0).deleteFromRealm(); // indirectly delete object    }});for (User user : users) {    showUser(user); // Will crash for the deleted user}

为避免该问题,可以使用 RealmResults 的 deleteFromRealm() 方法:

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}

删除

你可以从查询结果中删除数据:

// obtain the results of a queryfinal RealmResults<Dog> results = realm.where(Dog.class).findAll();// All changes to data must happen in a transactionrealm.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线程中而感觉不到延迟。但如果需要进行非常复杂的查询或者在大量数据中进行查询,那么使用后台线程进行查询将会是一个不错的主意。

示例:查找名字为 “John” 或者 “Peter” 的用户。

创建异步查询
RealmResults<User> result = realm.where(User.class)                              .equalTo("name", "John")                              .or()                              .equalTo("name", "Peter")                              .findAllAsync();

请注意,这里的调用并不会阻塞,而是立即返回一个 RealmResults。这很类似于标准 Java 中 Future 的概念。查询将会在后台线程中被执行,当其完成时,之前返回的 RealmResults 实例会被更新。

如果你希望当查询完成、RealmResults 被更新时获得通知,你可以注册一个 RealmChangeListener。这个监听器会在 RealmResults 被更新时被调用(通常是在事务被提交后)。

注册回调
private RealmChangeListener callback = new RealmChangeListener<RealmResults<User>>() {    @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);}

请在退出 Activity 或者 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}

同步查询返回的 RealmResults 实例的 isLoaded 方法会永远返回 true。

强制装载异步查询

你可以选择性地等待异步查询完成,而这将会阻塞当前线程,使查询变成同步(与 Future.get() 类似的概念)。

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

你可以在 Looper 线程中使用异步查询。异步查询需要使用 Handler 来传递查询结果。在没有 Looper 的线程中使用异步查询会导致 IllegalStateException 异常被抛出。

原文链接

https://realm.io/docs/java/latest/#queries

1 0
原创粉丝点击