关系

任何两个RealmObject都可以链接在一起。

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
}

关系在Realm中开销不大。这意味着保持连接并不会影响速度,并且关系的内部实现在使用内存上也是相当高效的。

多对一

在类中简单的定义一个继承自RealmObject的子类作为成员即可:

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

每个联系人(Contact的实例)都有0或1个电子邮件(Email实例)。在Realm中,没有什么可以阻止你在多个联系人中使用相同的电子邮件对象,上述模型可以是多对一关系,但通常用于建模一对一关系。

设置RealmObject字段null将清除引用,但对象不会从Realm中删除

多对多

你可以通过RealmList\字段声明从单个对象与任意数量的对象建立关系。例如,假设有多个电子邮件地址的联系人

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

public class Email extends RealmObject {
    public String address;
    public boolean active;
}

RealmLists基本上是RealmObjects的容器,并且RealmList和一个普通的Java List用法基本相同。Realm在不同的RealmLists中可以使用相同的对象两次(或更多),没有限制,因此你可以使用它来建模一对多和多对多关系。

你可以创建对象,并使用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);
    }
});

可以声明递归关系,当建模某些类型的数据时,这些关系可能很有用

public class Person extends RealmObject {
    public String name;
    public RealmList<Person> friends;
    // Other fields…
}

将RealmList字段的值设置null将清除列表。也就是说,列表将为空(长度为零),但没有对象会被删除。通过getter方法获取RealmList将永远不会返回null。返回的对象总是一个列表,但长度可能为零。

连接查询

查询连接和关系时可以的。考虑如下模型:

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可以和狗建立一对多的关系,正如下表所示:

这里写图片描述

使用连接查询来找一些人

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

首先,请注意,条件中的字段名称"equalTo"包含通过关系的路径(以句点分隔.)。

上述查询应读取,查找到拥有名为"布朗"的狗的所有人。重要的是要理解,结果将包含不满足条件的Dog对象,因为它们是Person对象的一部分。

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

注意第一个查询如何返回这两个Person对象,因为条件与两个人都匹配。每个Person查询结果都包含一个Dog对象列表- 它们的所有Dog对象(即使是那些不满足原始查询条件的对象)。记住,我们正在寻找有特定种类的Dog(姓名和颜色)的人,而不是实际的狗本身。因此,第二个查询将针对第一个Person查询result(r1)和每个Persons狗进行评估。第二个查询也匹配两个人,但这次是因为狗的颜色。

让我们深入一点,以帮助巩固这个概念。请查看以下示例:

/ 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"狗的 Person,也找到那些有名为"Brown"狗的Person,然后给我两个的​​交集。第二个查询先读取并查找到所有名为“Fluffy”的狗的Person。然后,给定结果集,找到所有有颜色为“棕色”的狗的Person,并且给定结果集,找到具有颜色为“黄色”的狗的所有人。

让我们来看看背后的查询,r1以便充分了解发生了什么。两个条件是equalTo("dogs.name", "Fluffy")和equalTo("dogs.color", "Brown")。第一个条件满足U1和U2- 这是C1集合。第二个条件也满足U1和U2- 这是C2集合。查询中的逻辑和,和C1和C2的交集是一样的。C1和C2的交集为U1 and U2。因此,r1是U1 and U2。

后面的查询r2就不同了。让我们开始分解这次查询以探究竟。查询的第一部分如下所示:RealmResults\ r2a = realm.where(Person.class).equalTo("dogs.name", "Fluffy").findAll();。它匹配U1和U2。然后,r2b = r2a.where().equalTo("dogs.color", "Brown").findAll();还是匹配U1和U2(两个人都有棕色的狗)。最后的查询,r2 = r2b.where().equalTo("dogs.color", "Yellow").findAll();只匹配U2,因为只有一个人在棕色狗结果集中有一个黄色的狗,也就是U2

读操作是隐式的,这意味着可以随时访问和查询对象。所有写操作(添加,修改和删除对象)必须包含在写事务中。写事务可以提交或取消。在提交期间,所有更改都将写入磁盘,并且提交只有在所有更改都可以保留时才会成功。通过取消写入事务,所有更改都将被丢弃。使用写事务,你的数据将始终处于一致状态。

写事务也用于确保线程安全

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

realm.beginTransaction();

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

realm.commitTransaction();

在写事务中使用到你的RealmObjects时,你可能最后想放弃修改。与其提交之后再回退,你可以简单地取消写事务。

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

//  ...

realm.cancelTransaction();

请注意,写事务可能会相互阻塞。如果你在UI线程和后台线程上同时创建写入事务,这可能会导致ANR错误。为了避免这种情况,在UI线程上创建写事务时使用异步事务。

Realm是崩溃安全的,所以在一次事务中发生了Exception,Realm本身不会崩溃。只是当前事务中的数据会丢失。如果异常被捕获但应用程序仍在继续,这个时候取消正在执行的事务就显得尤为重要。如果使用executeTransaction(),这些将会自动的执行。

感谢Realm的MVCC架构,在写事务打开的时候读取不会被阻塞!这意味着,除非你需要从多个线程同时进行事务,你可以使用一个更大的事务,这些事务如果使用小事务来做的话将会耗费更多的工作量。当你将写事务提交到Realm时,该Realm的所有其他实例将被通知并自动更新

Realm中的读写访问是ACID

创建对象

因为RealmObjects都得绑定到一个领域,他们应该通过Realm直接实例化

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

或者,你可以首先创建一个对象的实例,然后使用realm.copyToRealm())添加它。Realm支持尽可能多的自定义构造函数,只要其中一个是公共的无参数构造函数

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管理,所以对原始对象的任何进一步的更改都不会被持久化

事务块

不用手动保持跟踪realm.beginTransaction(),realm.commitTransaction()以及realm.cancelTransaction()可以使用realm.executeTransaction())方法,它会自动处理开始/提交,发生错误的时候将自动取消

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

异步事务

因为事务被其他事务阻塞,所以在后台线程上执行所有写操作以避免阻塞UI线程是一个很好的主意。通过使用异步事务,Realm将在后台线程上运行该事务并在事务完成时报告。

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控制,因此它们只允许在Looper线程上回调。

ealmAsyncTask 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,此对象可用于取消任何挂起的事务。如果回调更新UI,忘记取消事务可能会导致应用程序崩溃。

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

更新字符串和字节数

Realm是以完整的字段作为操作对象的,不可能更新字符串或字节数组的单个元素。假如你需要更新字符串的第5个元素,你将需要做类似的事情:

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        bytes[] bytes = realmObject.binary;
        bytes[4] = 'a';
        realmObject.binary = bytes;
    }
});

是由于Realm的MVCC架构,它避免了对现有数据的改变,以确保读取数据的其他线程或进程下表现一致。

快照

所有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参数增加时,它将错过一个元素。

为了防止这种情况,你可以拍摄集合数据的快照。快照保证元素的顺序不会改变,即使元素被删除或修改。

Iterator从Realm集合创建的文件将自动使用快照。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();

原文链接

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

results matching ""

    No results matching ""