Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to get Realm object by its primary key in Android Java

I wonder if there's a proper way to retrieve an object given its primary key in Realm for Android. I know the method objectForPrimaryKey does exists in Swift but there seems to be no such counterpart in Realm for Android. I really think that doing realm.where(EventInfo.class).equalTo("id", eventInfo.id).findFirst(); looks like a lot of waste (at least it is not wrist-friendly). Am I missing some method? I'm Currently using Realm 1.0.1

like image 588
Loudenvier Avatar asked Jun 29 '16 20:06

Loudenvier


People also ask

How to set primary key in Realm Android?

Primary key values are immutable. To change the primary key value of an object, you must delete the original object and insert a new object with a different primary key value. You cannot change the primary key field for an object type after adding any object of that type to a realm.

What is Realm schema?

An object schema is a configuration object that defines the fields and relationships of a Realm object type. Android Realm applications define object schemas with Java or Kotlin classes using Realm Schemas.


2 Answers

Am I missing some method?

Nope. As beeender mentioned, it's not implemented currently. Progress/discussion can be tracked here.

A helper function could look like this

public class Cat extends RealmObject {

    @PrimaryKey
    private int id;
    private String name;

    public Cat getByPrimaryKey(Realm realm, int id) {
        return realm.where(getClass()).equalTo("id", id).findFirst();
    }
}

It a class-specific method here because each class could have a different primary key type. You have to pass a realm instance to it that you manage in the calling class.

like image 163
Tim Avatar answered Sep 16 '22 15:09

Tim


I've ended up creating a helper class for this. I'll be using it until the Realm team implement these methods. I've named the class Find (you can rename it the way you like, since naming things and cache invalidation are the hardest things in computer science). I think it's better to use this than to call where().equalTo() passing the name of the primary key as a string value. This way you're sure to use the correct primary key field. Here is the code:

import java.util.Hashtable;
import io.realm.Realm;
import io.realm.RealmModel;
import io.realm.RealmObjectSchema;

public final class Find {
    // shared cache for primary keys
    private static Hashtable<Class<? extends RealmModel>, String> primaryKeyMap = new Hashtable<>();

    private static String getPrimaryKeyName(Realm realm, Class<? extends RealmModel> clazz) {
        String primaryKey = primaryKeyMap.get(clazz);
        if (primaryKey != null)
            return primaryKey;
        RealmObjectSchema schema = realm.getSchema().get(clazz.getSimpleName());
        if (!schema.hasPrimaryKey())
            return null;
        primaryKey = schema.getPrimaryKey();
        primaryKeyMap.put(clazz, primaryKey);
        return primaryKey;
    }

    private static <E extends RealmModel, TKey> E findByKey(Realm realm, Class<E> clazz, TKey key) {
        String primaryKey = getPrimaryKeyName(realm, clazz);
        if (primaryKey == null)
            return null;
        if (key instanceof String)
            return realm.where(clazz).equalTo(primaryKey, (String)key).findFirst();
        else
            return realm.where(clazz).equalTo(primaryKey, (Long)key).findFirst();
    }

    public static <E extends RealmModel> E byKey(Realm realm, Class<E> clazz, String key) {
        return findByKey(realm, clazz, key);
    }

    public static <E extends RealmModel> E byKey(Realm realm, Class<E> clazz, Long key) {
        return findByKey(realm, clazz, key);
    }
}

Usage is straightforward:

// now you can write this
EventInfo eventInfo = Find.byKey(realm, EventInfo.class, eventInfoId);
// instead of this
EventInfo eventInfo = realm.where(EventInfo.class).equalTo("id", eventInfo.id).findFirst();

It'll return null if there is no primary key for the given object or if the object is not found. I considered throwing an exception if there were no primary key, but decided it was overkill.

I was really sad Java generics are not as powerful as C# generics, because I really, really would love to call the method as follows:

Find.byKey<EventInfo>(realm, eventInfoId);

And believe me I tried! I've searched everywhere how to get a method's generic type return value. When it proved impossible, since Java erases the generic methods, I tried creating a generic class and use:

(Class<T>)(ParameterizedType)getClass()
        .getGenericSuperclass()).getActualTypeArguments()[0];

And all the possible permutations to no avail! So I gave up...

Note: I've only implemented String and Long versions of Find.byKey because Realm accepts only String and Integral data as primary keys, and Long will allow querying for Byte and Integer fields too (I hope!)

like image 29
Loudenvier Avatar answered Sep 18 '22 15:09

Loudenvier