Realm Java 3.1: Object Notifications, Backup Recovery and Reverse Relationships

Realm files read or written by this version cannot be opened with previous versions of Realm. Existing files will automatically be upgraded when they are opened. Please be careful about shipping updates to your existing apps!

We’re releasing version 3.1 of Realm Java today, and with it introducing better backup recovery for synchronized Realms, fine-grained object notifications for all Realms, and initial support for reverse relationships. Read on for all the details.

Fine-grained Object Notifications

In Realm Java 3.0, we released fine-grained collection notifications that made it possible to get detailed information about changes to collections. In 3.1, we extend that capability to single object notifications as well with the new RealmObjectChangeListener interface and the ObjectChangeSet class.

Person p = realm.where(Person.class).findFirst();
p.addChangeListener(new RealmObjectChangeListener<Person>() {
   @Override
   public void onChange(Person person, ObjectChangeSet changeSet) {
       if (changeSet.isDeleted()) {
           hide(); // Object was deleted
       } else {
           // Use information about which fields changed to only update part of the UI
           if (changeSet.isFieldChanged("name")) {
               updateName(person.getName());
           }
       }
   }
});

In past releases, single object notifications have had a lot of false positives, in the sense that the listener would trigger even though nothing had changed. This has also been fixed in 3.1, so now single object notifications, fine-grained or not, should only trigger if something in the object changed.

Better Backup Recovery

When your servers go down, you need a plan to recover. That’s why the Realm Mobile Platform has offered the ability to backup your data for a few months now. Under normal conditions, Realm’s synchronization engine works by transferring just the specific operations. When the Realm Object Server confirms the receipt of new operations, the local logs are cleaned up. This helps keep your app’s disk usage small and Realm blazing fast ⚡️.

Furthermore, because Realm is an offline-first database, if your Realm Object Server is down for whatever reason, all your local data stays available.

However, if you need to recover from a backup on your server, your clients will receive a “client reset” error via the sessions error handler. Note that you may continue to use the local Realm as you normally would, but that any subsequent changes, or changes made after the last backup point, will be lost.

Once you receive a “client reset” error, you could inform the user about the situation, stop accessing the Realm and redownload the Realm from the server at the latest backed up version. The ClientResetRequiredError class contains information and methods you can call for manually cleaning up the previous local version of the Realm.

If you choose to not handle the client reset error immediately, the next time your app launches, the previous local version of the Realm will be deleted and redownloaded from the server at the latest backed up version automatically on first access.

final SyncConfiguration config = new SyncConfiguration.Builder(user , url)
       .errorHandler(new SyncSession.ErrorHandler() {
           @Override
           public void onError(SyncSession session, ObjectServerError error) {
               if (error.getErrorCode() == ErrorCode.CLIENT_RESET) {
                   ClientResetRequiredError err = (ClientResetRequiredError) error;
                   closeRealm();
                   err.executeClientReset(); // Manually do the reset
                   err.getBackupFile(); // Reference to backed up file
               } else {
                   // Handle other errors
               }
           }
       })
       .build();

Inverse Relationships (BETA)

In Realm relationships between objects are bidirectional, which means that if you have a reference from Person to his/her Dog, then Realm automatically establishes a relationship in the other direction as well, and will automatically maintain it.

This inverse relationship is normally hidden in your model classes, but today we are shipping a new @LinkingObjects annotation that can make that inverse relationship visible so you can navigate through it.

In order to define an inverse relationship you need to do the following:

Add a RealmResults field with the generic type of the parent object. The field must be final. Realm will automatically fill it out for managed objects. Un-managed objects do not support inverse relationships. Add the @LinkingObjects annotation with the name of the field on the parent object that points to the model class defining the inverse relationship.

Take this example:

public class Person extends RealmObject {
   public String name;
   public int age;
   public Dog dog;
}  

public class Dog extends RealmObject {
   public String name;

   @LinkingObjects("dog")
   public final RealmResults<Person> owners = null;
}

// Use the inverse relationship 
Dog dog = realm.where(Dog.class).findFirst();
dog.owners.size(); // Find number of owners
dog.owners.first(); // Get a reference to the owner of the dog

The true power of reverse relationships will come when you can query across the inverse relationship and we are working hard on adding that for the next release.

Because implementation of queries has not yet been completed, we have marked the @LinkingObjects annotation as @Beta and would love your feedback on this feature.

File format upgrade

In 3.1 the internal Realm file format has been changed. Older Realm files will automatically be upgraded when opened, but doing so means that they no longer can be read by older versions of Realm.

See the CHANGELOG for the full list of changes.


Thanks for reading. Now go forth and build amazing apps with Realm! As always, we’re around on Stack Overflow, GitHub, or Twitter.