All fetches (including queries) are lazy in Realm, and the data is never copied.
所有获取(包括查询)在Realm中都是惰性的,并且永远不会复制数据。
Realm的查询引擎使用 Fluent interface 构造多子句查询。
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 query = realm.where(User.class);
// Add query conditions:
query.equalTo("name", "John");
query.or().equalTo("name", "Peter");
// Execute the query:
RealmResults result1 = query.findAll();
// Or alternatively do the same all at once (the "Fluent interface"):
RealmResults result2 = realm.where(User.class)
.equalTo("name", "John")
.or()
.equalTo("name", "Peter")
.findAll();
这为您提供了RealmResults类的新实例,其中包含名称为John或Peter的用户。
方法findAll
执行查询; [RealmQuery] []包含一整套findAll
方法:
findAll
查找满足查询条件的所有对象findAllAsync
在后台线程上异步运行findFirst
(和findFirstAsync
)查找符合查询条件的第一个对象有关完整的详细信息,请深入研究RealmQuery API参考。
查询返回对匹配对象的引用列表,因此您可以直接使用与查询匹配的原始对象。RealmResults从AbstractList继承,并且行为类似。例如,RealmResults是有序的,您可以通过索引访问单个对象。如果查询不匹配,则返回的RealmResults对象将是size(0)的列表(不为null)。
如果要修改或删除RealmResults集中的对象,则必须在写事务中进行。
请注意,您还可以查询关系:阅读有关链接查询的信息。
where方法通过指定model来启动RealmQuery。过滤条件是由谓词方法指定的,大多数谓词方法都具有不言自明的名称(例如equalTo)。谓词始终将字段名称作为其第一个参数。
并非所有谓词都可用于所有字段类型。有关详细信息,请查阅[RealmQuery] [] API参考。
对于所有数据类型,您具有以下谓词:
equalTo
notEqualTo
in
要将字段与值列表匹配,请使用in
。例如,要查找名称“Jill”\“ William”或“ Trillian”,可以使用in(“name”,new String [] {“ Jill” ,“ William”,“ Trillian”})
。 in谓词适用于字符串,二进制数据和数字字段(包括日期)。
包括日期在内的数值数据类型允许以下附加谓词:
between
(包括both和points等,是一个有界区间)greaterThan
lessThan
greaterThanOrEqualTo
lessThanOrEqualTo
字符串字段允许这些其他谓词:
contains
beginsWith
endsWith
like
所有四个字符串谓词都有一个可选的第三个参数来控制区分大小写:Case.INSENSITIVE和Case.SENSITIVE。默认值为Case.SENSITIVE。
谓词like执行glob样式的通配符匹配。匹配模式由字符和一个或多个通配符组成:
*
匹配0个或更多Unicode字符?
匹配单个Unicode字符例如,考虑一个具有四个对象的Realm,这些对象的字段名为name,其值分别为William,Bill,Jill和Trillian。谓词like(“name”,“?ill *”)将匹配前三个对象,而like(“name”,“ * ia?”)则将匹配第一个和最后一个对象。
二进制数据,字符串和RealmObjects列表(RealmList)可能为空,即长度为零。您可以使用以下方法检查是否为空:
isEmpty
isNotEmpty
如果不需要字段,则该值可以具有null值(请注意,永远不需要RealmObjects字段,并且该值可以为null)。您可以使用以下方法检查是否为空:
isNull
isNotNull
条件通过and与隐式连接。逻辑或联接必须使用or显式应用。
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 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”的用户,则查询可以是:
RealmResults r = realm.where(User.class)
.not()
.beginGroup()
.equalTo("name", "Peter")
.or()
.contains("name", "Jo")
.endGroup()
.findAll();
不过,通过此特定查询,它in可以更轻松地用于:
RealmResults r = realm.where(User.class)
.not()
.in("name", new String[]{"Peter", "Jo"})
.findAll();
您可以定义使用sort方法进行查询时应如何对结果进行排序。
RealmResults result = realm.where(User.class).sort("age").findAll();
或者,您可以对Realm已检索到的所有结果进行排序:
result = result.sort("age"); // Sort ascending
result = result.sort("age", Sort.DESCENDING);
默认情况下,排序是递增的;要更改此设置,请使用Sort.DESCENDING作为第二个参数。可以同时使用多个字段进行排序。
其他大多数数据库技术都可以“查询”查询结果(例如SQLite中的“ LIMIT”关键字)。通常这样做是出于避免从磁盘读取过多或一次将太多结果拖入内存的必要。
由于Realm中的查询是惰性的,因此在使用本地Realm文件时通常不需要执行这种分页,因为Realm仅在显式访问对象后才从查询结果中加载对象。
但是,在某些情况下,限制结果可能会有所帮助:
在这些情况下,可以通过将limit()关键字应用于查询来限制查询结果。
RealmResults people = realm.where(Person.class)
.sort("name")
.limit(10)
.findAllAsync();
像其他查询结果一样,有限的查询结果也会自动更新。这意味着,如果在查询第一次返回时返回了10个元素,则可以在基础数据集更改时替换或删除这10个对象。
当使用细粒度的通知时,不再属于RealmResults的对象将被报告为已删除。这不一定意味着它们已从基础Realm中删除,只是它们不再是查询结果的一部分。
关键字distinct(),sort()和limit()将按照指定的顺序应用。根据数据集,这可能会影响查询结果。通常,limit()应该最后应用。
目前尚不支持抵消有限的查询结果。在此跟踪此功能。
要仅返回唯一值,请使用distinct
。 例如,要了解您的realm中有多少个不同的名称:
RealmResults unique = realm.where(Person.class).distinct("name").findAll();
您只能在整数和字符串字段上调distinct
;其他字段类型将引发异常。与排序一样,您可以指定多个字段。
您可以对结果集运行其他查询:
RealmResults 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 dogs;
// getters & setters ...
}
您可以查询年龄在13到20岁之间且至少有一只1岁的狗的人:
RealmResults teensWithPups = realm.where(Person.class).between("age", 13, 20).equalTo("dogs.age", 1).findAll();
请注意,查询链建立在RealmResults之上,而不是RealmQuery。当您向RealmQuery对象添加更多条件时,您正在修改查询本身。
可以查询链接或关系。 考虑以下模型:
public class Person extends RealmObject {
private String id;
private String name;
private RealmList dogs;
// getters and setters
}
public class Dog extends RealmObject {
private String id;
private String name;
private String color;
// getters and setters
}
现在,我们可以通过链接查询找到特定的人:
// persons => [U1,U2]
RealmResults persons = realm.where(Person.class)
.equalTo("dogs.color", "Brown")
.findAll();
equalTo中的字段名称是使用句点(.)作为分隔符的关系之间的路径。上面的查询显示为“查找所有拥有颜色为棕色的狗的人。” 请注意,结果将包含具有至少一个匹配Dog的Person对象的所有Dog对象:
persons.get(0).getDogs(); // => [A,B]
persons.get(1).getDogs(); // => [B,C,D]
请记住,我们是在寻找有特定种类狗的人,而不是真正的狗。
让我们再深入一点:
// r1 => [U1,U2]
RealmResults r1 = realm.where(Person.class)
.equalTo("dogs.name", "Fluffy")
.equalTo("dogs.color", "Brown")
.findAll();
// r2 => [U2]
RealmResults r2 = realm.where(Person.class)
.equalTo("dogs.name", "Fluffy")
.findAll()
.where()
.equalTo("dogs.color", "Brown")
.findAll()
.where()
.equalTo("dogs.color", "Yellow")
.findAll();
第一个查询为:查找所有拥有名为“Fluffy”的狗并且颜色为“Brown”的人。
第二个查询为:
找到所有具有名为“Fluffy”的狗的人。
在该结果集中,找到 所有拥有颜色为“Brown”的狗的人。
然后,在该结果集中,找到所有拥有颜色为“黄色”的狗的人。
因此,第一个查询将找到两组Persons并返回这些集合的交集;第二个查询的操作方式有所不同,方法是获取每个findAll的结果集,并将其输入到下一个查询中,以依次缩小结果范围。您可以通过链接重写第二个查询:
RealmResults set1 = realm.where(Person.class).equalTo("dogs.name", "Fluffy").findAll();
RealmResults set2 = set1.where(Person.class).equalTo("dogs.color", "Brown").findAll();
RealmResults set3 = set2.where(Person.class).equalTo("dogs.color", "Yellow").findAll();
使用逆关系,可以扩展查询的可能性。让我们考虑相同的两个模型类,“Person”和“Dog”。您可以先查询Dog,然后按照与Person的逆关系来查询Dog,而不用用Person来开始查询。
RealmResults brownFluffies = realm.where(Dog.class).equalTo("color", "Brown").equalTo("name", "Fluffy").findAll();
for (Dog brownFluffy : brownFluffies) {
RealmResults owners = brownFluffy.getOwners();
// ...
}
您还可以使用具有反向关系的链接查询:
RealmResults dogs = realm.where(Dog.class).equalTo("persons.name", "Jane").findAll();
RealmResults是实时的,可自动将视图更新为基础数据。如果另一个线程,进程甚至设备修改了RealmResults集中的对象,则更改会立即反映出来。您的代码无需重新运行查询或手动刷新数据。
final RealmResults puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.size(); // => 0
realm.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 results) {
// results and puppies point are both up to date
results.size(); // => 1
puppies.size(); // => 1
}
});
这适用于所有RealmResults:过滤和链接的所有对象。
RealmResults的此属性不仅使Realm保持快速高效,而且还使您的代码更简单,反应更灵敏。例如,如果您的Activity/Fragment依赖于查询结果,则可以仅将Realm对象或RealmResults存储在字段中。访问时其数据将始终是最新的。
即使您不必刷新RealmResults,您的App也可能需要在数据更改时更新其UI或运行其他任务。您可以在Realm数据更新时订阅notifications。由于结果是自动更新的,因此请不要依赖索引和计数保持不变,这一点很重要。
RealmResults为汇总结果之间的求和和平均值提供了方便的聚合方法。
RealmResults 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();
所有Realm结果集都是实时的。这意味着它们始终反映最新状态。在大多数情况下,这样做是合乎需要的,但是如果要遍历集合以修改元素的目的怎么办? 例如:
RealmResults guests = realm.where(Person.class).equalTo("invited", false).findAll();
realm.beginTransaction();
for (int i = 0; guests.size(); i++) {
guests.get(i).setInvited(true);
}
realm.commitTransaction();
通常,您希望此简单循环邀请所有来宾。由于RealmResults会立即更新,因此最终只有一半的来宾被邀请! 被邀请的来宾将立即从集合中删除,这将转移所有元素。当i参数增加时,它将丢失一个元素。
为防止这种情况,您可以对集合数据进行快照。快照保证即使删除或修改了元素,元素的顺序也不会改变。
从RealmResults创建的迭代器将自动使用快照,而从RealmList创建的迭代器则不会。要在迭代RealmList时删除元素,应使用Iterator.remove()
代替RealmList.remove()
或其他会间接从RealmList中删除元素的API,以避免ConcurrentModificationException
。RealmResults和RealmList具有createSnapshot
方法来手动创建一个方法。
RealmResults guests = realm.where(Person.class).equalTo("invited", false).findAll();
// Use an iterator to invite all guests
realm.beginTransaction();
for (Person guest : guests) {
guest.setInvited(true);
}
realm.commitTransaction();
// Use a snapshot to invite all guests
realm.beginTransaction();
OrderedRealmCollectionSnapshot guestsSnapshot = guests.createSnapshot();
for (int i = 0; guestsSnapshot.size(); i++) {
guestsSnapshot.get(i).setInvited(true);
}
realm.commitTransaction();
您可以从realm删除查询结果:
// 获取查询结果
final RealmResults results = realm.where(Dog.class).findAll();
// 数据的所有更改都必须在事务中发生
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// 删除单场比赛
results.deleteFirstFromRealm();
results.deleteLastFromRealm();
// 删除单个对象
Dog dog = results.get(5);
dog.deleteFromRealm();
// 删除所有比赛
results.deleteAllFromRealm();
}
});
Realm中的大多数查询都足够快,即使在UI线程上也可以同步运行。但是,对于复杂查询或对大型数据集的查询,在后台线程上运行查询可能是一个优势。
RealmResults result = realm.where(User.class)
.equalTo("name", "John")
.or()
.equalTo("name", "Peter")
.findAllAsync();
请注意,查询没有阻塞-它会立即返回RealmResults
。这与标准Java中的Future的概念类似。该查询将继续在后台线程中运行,并在完成后更新返回的RealmResults实例。
如果您希望在查询完成并且RealmResults对象更新时收到通知,则可以注册RealmChangeListener
。每当RealmResults更新以反映Realm中的最新更改时(通常是在提交之后),都会调用此侦听器。
private OrderedRealmCollectionChangeListener> callback = new OrderedRealmCollectionChangeListener<>() {
@Override
public void onChange(RealmResults results, OrderedCollectionChangeSet changeSet) {
if (changeSet == null) {
// The first time async returns with an null changeSet.
} else {
// Called on every update.
}
}
};
private RealmResults result;
public void onStart() {
result = realm.where(User.class).findAllAsync();
result.addChangeListener(callback);
}
记住在退出Activity/Fragment时注销所有侦听器,以避免内存泄漏。
public void onStop () {
result.removeChangeListener(callback); // remove a particular listener
// or
result.removeAllChangeListeners(); // remove all registered listeners
}
使用isLoaded
检查查询是否已完成:
RealmResults result = realm.where(User.class).findAllAsync();
if (result.isLoaded()) {
// Results are now available
}
在同步获取的RealmResults对象上调用isLoaded将始终返回true。
您也可以等待查询完成。这将阻塞线程,使查询再次同步。
RealmResults result = realm.where(User.class).findAllAsync();
result.load() // be careful, this will block the current thread until it returns
注意:您只能在Looper线程上使用异步查询。异步查询需要使用Realm的Handler才能始终如一地交付结果。尝试使用在没有Looper的线程内打开的Realm调用异步查询将引发IllegalStateException
。