Skip to content

Instantly share code, notes, and snippets.

@carloseduardosx
Last active January 18, 2021 05:35
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save carloseduardosx/a7bd88d7337660cd10a2c5dcc580ebd0 to your computer and use it in GitHub Desktop.
Save carloseduardosx/a7bd88d7337660cd10a2c5dcc580ebd0 to your computer and use it in GitHub Desktop.
RealmAutoIncrement is a singleton which maintain the last id saved from each database model
package com.carlosedurdo.database;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import io.realm.Realm;
import io.realm.RealmObject;
/**
* Implementation of Auto Increment feature for Realm
* RealmAutoIncrement is a singleton which maintain the last id saved from each database model.
*
* To get next id from anyone model, simple call the method getNextIdFromModel.
* @see #getNextIdFromModel(Class)
*/
public final class RealmAutoIncrement {
private Realm realm;
private Map<Class<? extends RealmObject>, AtomicInteger> modelMap = new HashMap<>();
private static RealmAutoIncrement autoIncrementMap;
private RealmAutoIncrement() {
}
{
realm = Realm.getDefaultInstance();
modelMap.put(SampleModel.class, new AtomicInteger(getLastIdFromModel(SampleModel.class)));
}
/**
* Utility method which query for all models saved and get the bigger model id saved
* Used to guarantee which the last model id saved is really the last
*
* @param clazz Model which should get the last id
* @return The last id saved from model passed
*/
private int getLastIdFromModel(Class<? extends RealmObject> clazz) {
String primaryKeyColumnName = "id";
Number lastId = realm.where(clazz).max(primaryKeyColumnName);
return lastId == null ? 0 : lastId.intValue();
}
/**
* Search in modelMap for the last saved id from model passed and return the next one
*
* @param clazz Model to search the last id
* @return The next id which can be saved in database for that model,
* {@code null} will be returned when this method is called by reflection
*/
public Integer getNextIdFromModel(Class<? extends RealmObject> clazz) {
if (isValidMethodCall()) {
AtomicInteger modelId = modelMap.get(clazz);
if (modelId == null) {
return 0;
}
return modelId.incrementAndGet();
}
return null;
}
/**
* Utility method to validate if the method is called from reflection,
* in this case is considered a not valid call otherwise is a valid call
*
* @return The boolean which define if the method call is valid or not
*/
private boolean isValidMethodCall() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTraceElements) {
if (stackTraceElement.getMethodName().equals("newInstance")) {
return false;
}
}
return true;
}
public static RealmAutoIncrement getInstance() {
if (autoIncrementMap == null) {
autoIncrementMap = new RealmAutoIncrement();
}
return autoIncrementMap;
}
}
@carloseduardosx
Copy link
Author

carloseduardosx commented Jun 15, 2016

Sample of how to use the RealmAutoIncrement

import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;

public class SampleModel extends RealmObject {

    @PrimaryKey
    private Integer id = RealmAutoIncrement.getInstance().getNextIdFromModel(SampleModel.class);

    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

Was need to set realm default configuration before use RealmAutoIncrement. Below has a sample of how to do that:

RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfiguration);

On android I recommend put that configuration in your custom application class

@bachhuberdesign
Copy link

Works perfectly, this seems like a solid solution until Realm can auto increment keys internally.

@svedie
Copy link

svedie commented Jun 26, 2016

Do I need to create a new class for every model? Cause in the getInstance() method thre is a fixed class.

@carloseduardosx
Copy link
Author

carloseduardosx commented Jul 4, 2016

@svedie Not really, RealmAutoIncrement is a singleton. Every call to RealmAutoIncrement.getInstance() does not create a new instance, but reuses the first instance created.

@svedie
Copy link

svedie commented Jul 12, 2016

Thanks!

@paulpv
Copy link

paulpv commented Aug 17, 2016

Thanks for your code.
I simplified it for my purposes to:

import android.support.annotation.NonNull;

import com.pebblebee.common.annotations.NonNullNonEmpty;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmModel;

/**
 * To use:
 * <pre>
 *     public class DataModel extends RealmObject
 *     {
 *         public static RealmUserModel createData(@NonNull Realm realm) {
 *             boolean wasInTransaction = realm.isInTransaction();
 *             if (!wasInTransaction) {
 *                 realm.beginTransaction();
 *             }
 *
 *             long id = RealmAutoIncrement.getNextIdFromModel(realm, DataModel.class, "id");
 *             RealmUserModel user = realm.createObject(DataModel.class, id);
 *
 *             if (!wasInTransaction) {
 *                 realm.commitTransaction();
 *             }
 *
 *             return user;
 *         }
 *
 *         {@literal @}PrimaryKey
 *         private long id;
 *         ...
 *     }
 *
 *     ...
 *     Realm realm = Realm.getDefaultInstance(); // <-- or however you get your instance
 *     ...
 *     DataModel data = DataModel.createData(realm);
 *     ...
 *     realm.close();
 *
 * </pre>
 */
public class RealmAutoIncrement
{
    public static long INVALID_ID = -1;

    private static final Map<RealmConfiguration, Map<Class<? extends RealmModel>, AtomicLong>> sModelMap = new HashMap<>();

    public static long getLastIdFromModel(
            @NonNull
            Realm realm,
            @NonNull
            Class<? extends RealmModel> clazz,
            @NonNullNonEmpty
            String fieldName)
    {
        Number lastId = realm.where(clazz).max(fieldName);
        return lastId != null ? lastId.intValue() : INVALID_ID + 1;
    }

    public static long getNextIdFromModel(
            @NonNull
            Realm realm,
            @NonNull
            Class<? extends RealmModel> clazz,
            @NonNullNonEmpty
            String fieldName)
    {
        RealmConfiguration realmConfiguration = realm.getConfiguration();
        Map<Class<? extends RealmModel>, AtomicLong> modelMap = sModelMap.get(realmConfiguration);
        if (modelMap == null)
        {
            modelMap = new HashMap<>();
            sModelMap.put(realmConfiguration, modelMap);
        }

        AtomicLong modelId = modelMap.get(clazz);
        if (modelId == null)
        {
            modelId = new AtomicLong(getLastIdFromModel(realm, clazz, fieldName));
            modelMap.put(clazz, modelId);
        }

        return modelId.incrementAndGet();
    }
}

@carloseduardosx
Copy link
Author

carloseduardosx commented Sep 3, 2016

I was update the implementation of Auto Increment feature for Realm. Now it's much more robust with support for large operations of insert and delete at same time.

Everyone can see the new implementation here:
https://gist.github.com/carloseduardosx/6c3a3dfc3a9cb467aa464884f39544c2

See: Sample of how to use RealAutoIncrement. Is pretty simple too 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment