Search code examples
androidrealm

How to do a Realm migration in Android?


I'm using Realms as a database in Android app. Works fine, but I've added a new label in my user model and I'm getting the error that I need to migrate my schema:

java.lang.RuntimeException: Unable to create application com.apelucy.apelucy.app.base.MyApplication: io.realm.exceptions.RealmMigrationNeededException: Migration is required due to the following errors:
                                                   - Property 'User.testRealm' has been added.

How can I do the migration? I've found other solutions here but I can't implement them in my code. I can't use a solution of delete and install the app. I now that work in development, but I need to update the app in production.

My UserRespository class:

public class UserRepository {

    private static UserRepository sInstance = null;
    private Context mContext = null;



    public static UserRepository getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new UserRepository();
            sInstance.mContext = context;
        }

        return sInstance;
    }

    // DATABASE Methods

    public void storeUser(final User user) {
        AppSingleton.getInstance().setUser(user);

        Realm realm = null;
        try {
            realm = Realm.getDefaultInstance();
            realm.executeTransaction(realm1 -> realm1.insertOrUpdate(user));
        } finally {
            if (realm != null) {
                realm.close();
            }
        }
    }

    public User retrieveUser() {
        Realm realm = null;
        User user = null;
        try {
            realm = Realm.getDefaultInstance();
            User userRealmResult = realm.where(User.class)
                    .findFirst();

            if (userRealmResult != null) {
                user = realm.copyFromRealm(userRealmResult);
            }

        } finally {
            if (realm != null) {
                realm.close();
            }
        }

        return user;
    }

    public void clearUser() {
        // Clear Database objects
        Realm realm = null;
        try {
            realm = Realm.getDefaultInstance();
            realm.executeTransaction(realm1 -> realm1.delete(User.class));
        } finally {
            if (realm != null) {
                realm.close();
            }
        }
    }
}

Init realm in my Application:

Realm.init(this);

My model change:

@SerializedName("test")
@Expose
private String testRealm;

Solution

  • Migrations allow you to modify the schema of the application, which means that it lets you add, remove, rename tables/fields in the Realm schema. If you change a RealmModel class, then you must write the migration that will map the existing Realm file to reflect the new model classes.

    RealmConfiguration config = new RealmConfiguration.Builder()
                                     .schemaVersion(1)
                                     .migration(new MyMigration()) 
                                     .build();
    Realm.setDefaultConfiguration(config);
    

    The default schema version is 0.


    Migrations are fairly straightforward:

    • you must increment the schema version, so Realm knows you want to increment the schema's version to a specific number

    • you must supply a migration that will handle the change from one version to another

    Migrations describe the operations to do when you need to go from one schema version to another:

    public class MyMigration implements RealmMigration {
    
        @Override
        public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
            RealmSchema schema = realm.getSchema();
    
            // Migrate from version 0 to version 1
            if (oldVersion == 0) {
                RealmObjectSchema userSchema = schema.get("User");
    
                userSchema.addField("testRealm", String.class);
                oldVersion++;
            }
    
            if (oldVersion == 1) { // ...
                // ...
            }
        }
    
        @Override
        public int hashCode() { return MyMigration.class.hashCode(); }
    
        @Override
        public boolean equals(Object object) { return object != null && object instanceof MyMigration; }
    }