Realm(Java)數據庫使用文檔(查詢Queries)


Realm(Java)數據庫使用文檔(目錄)

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<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類的新實例,其中包含名稱爲John或Peter的用戶。

方法findAll執行查詢; [RealmQuery] []包含一整套findAll方法:

  • findAll查找滿足查詢條件的所有對象
  • findAllAsync在後臺線程上異步運行
  • findFirst(和findFirstAsync)查找符合查詢條件的第一個對象

有關完整的詳細信息,請深入研究RealmQuery API參考。

查詢返回對匹配對象的引用列表,因此您可以直接使用與查詢匹配的原始對象。RealmResults從AbstractList繼承,並且行爲類似。例如,RealmResults是有序的,您可以通過索引訪問單個對象。如果查詢不匹配,則返回的RealmResults對象將是size(0)的列表(不爲null)。

如果要修改或刪除RealmResults集中的對象,則必須在寫事務中進行。

請注意,您還可以查詢關係:閱讀有關鏈接查詢的信息。

8.1 篩選

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

8.2 邏輯運算符

條件通過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<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”的用戶,則查詢可以是:

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

不過,通過此特定查詢,它in可以更輕鬆地用於:

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

8.3 排序

您可以定義使用sort方法進行查詢時應如何對結果進行排序。

RealmResults<User> result = realm.where(User.class).sort("age").findAll();

或者,您可以對Realm已檢索到的所有結果進行排序:

result = result.sort("age"); // Sort ascending
result = result.sort("age", Sort.DESCENDING);

默認情況下,排序是遞增的;要更改此設置,請使用Sort.DESCENDING作爲第二個參數。可以同時使用多個字段進行排序。

8.4 結果限制

其他大多數數據庫技術都可以“查詢”查詢結果(例如SQLite中的“ LIMIT”關鍵字)。通常這樣做是出於避免從磁盤讀取過多或一次將太多結果拖入內存的必要。

由於Realm中的查詢是惰性的,因此在使用本地Realm文件時通常不需要執行這種分頁,因爲Realm僅在顯式訪問對象後才從查詢結果中加載對象。

但是,在某些情況下,限制結果可能會有所幫助:

  • 使用基於查詢的Realm時,將在查詢數據時從服務器獲取數據。在這種情況下,限制結果數量是有意義的,因爲它直接影響從服務器傳輸的數據量。
  • 在創建依賴於有限查詢結果的用戶界面時,例如“十大列表”。在這種情況下,創建有限的查詢結果將降低創建此類屏幕所需代碼的複雜性。

在這些情況下,可以通過將limit()關鍵字應用於查詢來限制查詢結果。

RealmResults<Person> people = realm.where(Person.class)
  .sort("name")
  .limit(10)
  .findAllAsync();

像其他查詢結果一樣,有限的查詢結果也會自動更新。這意味着,如果在查詢第一次返回時返回了10個元素,則可以在基礎數據集更改時替換或刪除這10個對象。

當使用細粒度的通知時,不再屬於RealmResults的對象將被報告爲已刪除。這不一定意味着它們已從基礎Realm中刪除,只是它們不再是查詢結果的一部分。

關鍵字distinct(),sort()和limit()將按照指定的順序應用。根據數據集,這可能會影響查詢結果。通常,limit()應該最後應用。

目前尚不支持抵消有限的查詢結果。在此跟蹤此功能。

8.5 唯一值

要僅返回唯一值,請使用distinct。 例如,要了解您的realm中有多少個不同的名稱:

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

您只能在整數和字符串字段上調distinct;其他字段類型將引發異常。與排序一樣,您可以指定多個字段。

8.6 鏈式查詢(Chaining queries)

您可以對結果集運行其他查詢:

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歲之間且至少有一隻1歲的狗的人:

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

請注意,查詢鏈建立在RealmResults之上,而不是RealmQuery。當您向RealmQuery對象添加更多條件時,您正在修改查詢本身。

8.7 關聯查詢(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
}

每個Person對象都有多個Dog關係,如下表所示:
image

現在,我們可以通過鏈接查詢找到特定的人:

// persons => [U1,U2]
RealmResults<Person> 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<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();

第一個查詢爲:查找所有擁有名爲“Fluffy”的狗並且顏色爲“Brown”的人。

第二個查詢爲:
找到所有具有名爲“Fluffy”的狗的人。
在該結果集中,找到 所有擁有顏色爲“Brown”的狗的人。
然後,在該結果集中,找到所有擁有顏色爲“黃色”的狗的人。

因此,第一個查詢將找到兩組Persons並返回這些集合的交集;第二個查詢的操作方式有所不同,方法是獲取每個findAll的結果集,並將其輸入到下一個查詢中,以依次縮小結果範圍。您可以通過鏈接重寫第二個查詢:

RealmResults<Person> set1 = realm.where(Person.class).equalTo("dogs.name", "Fluffy").findAll();
RealmResults<Person> set2 = set1.where(Person.class).equalTo("dogs.color", "Brown").findAll();
RealmResults<Person> set3 = set2.where(Person.class).equalTo("dogs.color", "Yellow").findAll();

使用逆關係,可以擴展查詢的可能性。讓我們考慮相同的兩個模型類,“Person”和“Dog”。您可以先查詢Dog,然後按照與Person的逆關係來查詢Dog,而不用用Person來開始查詢。

RealmResults<Dog> brownFluffies = realm.where(Dog.class).equalTo("color", "Brown").equalTo("name", "Fluffy").findAll();
for (Dog brownFluffy : brownFluffies) {
    RealmResults<Person> owners = brownFluffy.getOwners();
    // ...
}

您還可以使用具有反向關係的鏈接查詢:

RealmResults<Dog> dogs = realm.where(Dog.class).equalTo("persons.name", "Jane").findAll();

8.8 自動更新結果

RealmResults是實時的,可自動將視圖更新爲基礎數據。如果另一個線程,進程甚至設備修改了RealmResults集中的對象,則更改會立即反映出來。您的代碼無需重新運行查詢或手動刷新數據。

final RealmResults<Dog> 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<Dog> 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。由於結果是自動更新的,因此請不要依賴索引和計數保持不變,這一點很重要。

8.9 聚合

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

8.10 迭代和快照

所有Realm結果集都是實時的。這意味着它們始終反映最新狀態。在大多數情況下,這樣做是合乎需要的,但是如果要遍歷集合以修改元素的目的怎麼辦? 例如:

RealmResults<Person> 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<Person> 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<Person> guestsSnapshot = guests.createSnapshot();
for (int i = 0; guestsSnapshot.size(); i++) {
    guestsSnapshot.get(i).setInvited(true);
}
realm.commitTransaction();

8.11 刪除

您可以從realm刪除查詢結果:

// 獲取查詢結果
final RealmResults<Dog> 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();
    }
});

8.12 異步查詢

Realm中的大多數查詢都足夠快,即使在UI線程上也可以同步運行。但是,對於複雜查詢或對大型數據集的查詢,在後臺線程上運行查詢可能是一個優勢。

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

請注意,查詢沒有阻塞-它會立即返回RealmResults <User>。這與標準Java中的Future的概念類似。該查詢將繼續在後臺線程中運行,並在完成後更新返回的RealmResults實例。

如果您希望在查詢完成並且RealmResults對象更新時收到通知,則可以註冊RealmChangeListener。每當RealmResults更新以反映Realm中的最新更改時(通常是在提交之後),都會調用此偵聽器。

private OrderedRealmCollectionChangeListener<RealmResults<User>> callback = new OrderedRealmCollectionChangeListener<>() {
    @Override
    public void onChange(RealmResults<User> results, OrderedCollectionChangeSet changeSet) {
        if (changeSet == null) {
            // The first time async returns with an null changeSet.
        } else {
            // Called on every update.
        }
    }
};

private RealmResults<User> 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<User> result = realm.where(User.class).findAllAsync();
if (result.isLoaded()) {
  // Results are now available
}

在同步獲取的RealmResults對象上調用isLoaded將始終返回true。

您也可以等待查詢完成。這將阻塞線程,使查詢再次同步。

RealmResults<User> 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章