Search code examples
javaandroidsqliterealmlinkingobjects

How does the @LinkingObjects annotation in Realm work


I have an Android app that uses SQLite with its json1 extension as its database backend. While this works the SQL required to manipulate the JSON is very hard to understand and even harder to maintain. Consequently, I am currently experimenting with Realm as an alternative. While Realm is for the most part intuitive LinkingObjects is a feature that I do not fully understand. Consider the following classes that I currently use

public class GridNode extends RealmObject
{
 @PrimaryKey
 private int id = 0;   
 @Index
 private int lx = 0;
 @Index
 private int ly = 0;

 @LinkingObjects("gridnode")
 private final RealmResults<PassPoint> passpoints = null;

 //getters, setters & constructors
}


public class PassPoint extends RealmObject
{
 private GridNode gridnode;//each passpoint refers to one distinct GridNode object
 private int hits;
 private int lastVisited;

 //getters, setters & constructors
} 

In the current SQLite version of my data I identify the GridNode used by each PassPoint by referencing its AUTO_INCREMENTing id field. With Realm things get much simpler since I can simply use the GridNode itself as an attribute of the PassPoint.

This is where things get a bit less clear. Suppose I retrieve an existing GridNode from a Realm by running a RealmQuery e.g.

myrealm.where(GridNode.class).equalTo("lx",23).equalTo("ly",32).findFirst();

Reading between the lines I have concluded that running this query will not only fetch the GridNode I am after but run an implicit query using my

@LinkingObjects("gridnode")
private final RealmResults<PassPoint> passpoints = null;

annotation to retrieve a list of all PassPoint objects that refer to the GridNode in question.

Very handy but I find myself wondering if this does not come at a price - the time required to run that query. And suppose I have a few other classes that also refer to GridNodes in which case I will have further @LinkingObjets annotation which would then result in further implicit queries?

As opposed to that if I were to simply record a GridNode id I could then deal with identifying the relevant GridNode myself as and when required? In effect, trading convenience for speed and responsiveness?

Or perhaps I have quite simply read wrongly between the lines and this is not at all how @LinkingObjects works?

Another thing that is not quite clear - as you will note gridnode is a private member of the PassPoint class. How then am I able to create the @LinkingObjects("gridnode") annotation in my GridNode class without the compiler complaining about me trying to access a member that is not visible from outside the PassPoint class?


Solution

  • Reading between the lines I have concluded that running this query will not only fetch the GridNode I am after but run an implicit query

    No. Not quite.

    As the Realm documentation states here:

    All fetches (including queries) are lazy in Realm, and the data is never copied.

    This means that the simple case of defining a query does just that - it defines it. But it does not evaluate/execute the query until the user specifically requests it. An object with a query field (e.g. a LinkingObjects field) therefore provides a simple method by which the user may execute a query, but does not execute it autonomously just because the object itself is loaded. It is only run when a findFirst or findAll (or similar) call is made.

    So if you never actually access the GridNode.passpoints field, it is never executed.

    You could prove this to yourself with a simple experiment:-

    1. Retrieve a GridNode from the Realm with no linked PassPoint.
    2. Access the linking objects field and check that it is empty.
    3. Add a new Passpoint to the realm with a reference to the same GridNode.
    4. Retest the linking objects field of the same object you retrieved in step 1, and you should see that the query result now includes the new Passpoint.

    As to your second question, the simple answer is that the protection level of your model fields applies only to the Java models of your data; it is not applied to the underlying Realm models.