Understanding Migrations in Realm Java using Android Studio

in utopian-io •  5 months ago

realmDark.jpg

Repository

https://github.com/realm/realm-java

What Will I Learn?

  1. How to use RealmConfiguration
  2. How to handle migrations in Realm. (Local Migrations)

Requirements

  • An Integrated Development Environment(IDE) for building Android Application(e.g Android Studio, IntelliJ)
  • Android Device/Virtual Device.
  • Little Experience in working with Realm Java.
  • Java Programming Experience.
  • Of course, willingness to learn

Resources

Difficulty

  • Intermediate
Tutorial Duration - 30 - 35Mins

Tutorial Content

In today's tutorial, we are going to be learning Migrations in Realm Database. Similar to working with any database, you are likely to change your model class which sometimes is called database schema overtime. Since model classes in Realm are as easy as defining standard objects, changing schemas is easy.

In order for Realm to be able to handle your change in your schema, Migrations are neccesary, meaning migrating from the Schema configuration saved in Realm to the new one you must altered which can be done adding a new field to your model class.

To understanding the concept behind Migration, we are going to be creating an android application which we will be defining a Model class at the beginning and then adding a new field once we have run the application once and then we will Migrate to the new Model class configuration.

Outline

  • Dependencied Used
  • Add a TextView in activity_main.xml layout file.
  • Create a model class - Person
  • Realm Migration Illustration

Depenedencies used

  • implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

The ButterKnife dependency should be placed in your application level gradle file - "build.gradle" which will make the injection of views (e.g ImageView, TextView) as easy as possible which we will be seeing in this tutorial.

  • implementation 'org.projectlombok:lombok:1.16.20'
    annotationProcessor 'org.projectlombok:lombok:1.16.20'

The lombok dependency also is placed in the application level gradle file which makes the generator of getter and setter methods for our model classes by just adding the annotations @Getter for getters and @Setter for the setter methods.

  • Realm dependency

    Steps

  1. Head to your project level gradle file and add the classpath dependency:
    classpath "io.realm:realm-gradle-plugin:5.1.0"

  2. Next, head to your application level Gradle file "build.gradle" and add the realm-android plugin to the top of the file.

apply plugin:'realm-android'

Finally, refresh your Gradle dependencies.

After you have added the necessary dependencies, your application level Gradle file should look like this :

app Gradle.PNG

And your project level Gradle file should look like this :

projectLevel.PNG

Add TextView in activity_main.xml

In order for us to show the result of our migrations in our tutorial, we are going to be adding one TextView in our activity_main.xml file :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Activities.MainActivity">

    <TextView
        android:id="@+id/result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_centerInParent="true"
        android:text="Hello World!" />
        
</RelativeLayout>

Code Explanation
The above code includes a TextView in our layout file with the id - result and places the text right in the center of the screen with - android:layout_centerInParent="true" and has a padding of 10 on every side - android:padding="10dp".

Creating the Person model Class

Next, we are going to create a new java class file which can be done by right-clicking on java folder => New => Java class and then input the name the class Person.

new java class file.PNG

Step 1

PersonClass.PNG

Step 2


Next, let the Person class extend the RealmObject class and add the following fields - name (String) , age (int) and then we add the annotations @Getter and @Setter using lombok in order to inject our getter and setter methods, this way boilerplate are eliminated.

@Getter
@Setter
public class Person extends RealmObject {
    private String name;
    private int age;
}

Realm Migration Illustration

In order for us to illustrate Realm Migration, we are going to be creating a realm database using a custom configuration where we are going to be specifying a schemaVerson and a Migration class that will run automatically if Migration is needed.

Migration helps you to change your realm file configuration while you retain the details in your realm database.

In order to illustrate Migration in our MainActivity.java class file, we are going to be creating a custom Realm Configuration , specify its schemaVersion and the Migration method.

Firstly, we have to create a Realm variable - private Realm realm; and then add the following codes in our onCreate() method

Realm.init(this);

RealmConfiguration customConfig = new RealmConfiguration.Builder()
        .schemaVersion(1) // Must be bumped when the schema changes
        .migration(new NewMigration())
        .build()

realm = Realm.getInstance(customConfig);

createRealmObjects();

private class NewMigration implements RealmMigration {
        @Override
        public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
            RealmSchema schema = realm.getSchema();

            if (oldVersion == 0) {
                schema.create("Person")
                        .addField("name", String.class)
                        .addField("age", int.class);
                oldVersion++;
            }
}

Code Explanation

  1. We initialize Realm in our Acitivy class file - Realm.init(this);
  2. We create a custom Realm configuration by the name - customConfig and then we indicate the schemaVerson as 1 and also specify the configuration method as NewMigration() which will be ran automatically once a Migration is needed.
  3. We then set our realmm variable to use our customConfig as its configuration - realm = Realm.getInstance(customConfig);.
  4. We then call the createRealmObjects(); which as its name implies creates Realm Objects.
  5. In our NewMigration() Method -
    • We override the migrate() method and use the DynamicRealm argument to get an editable schema - RealmSchema schema = realm.getSchema();.
    • We then check for the schema version if it's zero and it's our first migration, we create a Person schema and add the fields - name and age specifying their datatypes - String.class and int.class respectively.
    • We then increase the value of our oldVersion argument by one - oldVersion++.

createRealmObjects()

private void createRealmObjects() {
realm.executeTransaction(new Realm.Transaction() {
        @Override
        public void execute(Realm realm) {
            Person Eben = realm.createObject(Person.class);
            Eben.setName("Ebenezer");
            Eben.setAge(27);

            Person Jude = realm.createObject(Person.class);
            Jude.setAge(31);
            Jude.setName("Yanbor Jude");
        }
    });
    StringBuilder toDisplay = new StringBuilder();
        RealmResults<Person> results = realm.where(Person.class).findAll();
        for (Person result : results) {
        toDisplay.append("Name :"+result.getName() + " Age :" + result.getAge()+" \n \n");
        }
        resultTv.setText(toDisplay);
}

Code Explanation

  1. We start an executeTransaction() on our realm variable and then we override the execute() method where we create two new Person objects with the details
    • Object 1 (Eben) => name = Ebenezer , age = 27
    • Object 2 (Jude) => name = Yanbor , age = 31.
  2. Next, we declear a StringBuilder object - toDisplay.
  3. We then get all the objects that are of Person class and append their details to the StringBuilder object - toDisplay using a for each loop.
    • name - result.getName()
    • age - result.getAge().
  4. We then set text of our TextView injected by butterknife by - resultTv.setText(toDisplay.toString());

Application Execution Image

Screenshot_20180604-181801.png

Now, in our Person class, we want to include a new String field - birthDay which is supposed to hold the birthday in the format - "May 05", hence, our Person class should look like below -

NB:: The below code will only feature additions for brevity

//@Getter and @Setter annotations for lombok
public class Person extends RealmObject {
    //exisitng fields - name and age
    String birthDay;
}

When we try to run the application with the above changes, we get this error in our logcat -

logcat error.PNG

Which states that Migration is required due to the following reasons - property Person.birthDay has been added.

Now, in order to solve this issue, We need to make two changed to our MainActivity.java file :

  1. customConfig code block -
RealmConfiguration customConfig = new RealmConfiguration.Builder()
                .schemaVersion(2)
                .migration(new NewMigration())
                .build();

Code Explanation

  • All we do here is to increase our schemaVersion to 2 as that is needed for every new migration.

2.NewMigration() method

private class NewMigration implements RealmMigration {
        @Override
        public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
            RealmSchema schema = realm.getSchema();

            if (oldVersion == 1) {
                schema.get("Person")
                        .addField("birthDay", String.class);
                oldVersion++;
            }
}

Code Explanation

  • We check if the version number is 1 and if so, we use the DynamicRealm object ot get the Person class and then we use the addField method, passing the property we just added ("birthDay") as the first argument and the class type - String.class as the second argument.

3.CreateRealmObjects()

private void createRealmObjects() {
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Person Paul = realm.createObject(Person.class);
        Paul.setName("Paul");
        Paul.setAge(27);
        Paul.setBirthDay("May 05");

        Person Bola = realm.createObject(Person.class);
        Bola.setAge(31);
        Bola.setName("Bola");
        Bola.setBirthDay("June 06");
    }
});

StringBuilder toDisplay = new StringBuilder();
RealmResults<Person> results = realm.where(Person.class).findAll();
for (Person result : results) {
    toDisplay.append("Name :"+result.getName() + " Age :" + result.getAge()+" birthDay : "+result.getBirthDay()+" \n \n");
}
resultTv.setText(toDisplay.toString());
}

Code Explanation

  1. We create two new Person objects - Paul and Bola and then we set the nessary details including the newly created one - Paul.setBirthDay("May 05") and Bola.setBirthDay("June 6").
  2. We then add the birthDay field in our for each loop to display the new details.
    • name => result.getName()
    • age => result.getAge()
    • birthDay = > result.getBirthDay()
  3. And then lastly we set the text of our TextView injected by butterknife - resultTv.setText(toDisplay.toString());

Application Execution Image

Screenshot_20180604-190418.png

This shows that with migration, you can still maintain your objects in your realm and continue with newly added objects.

Curriculum

Proof of Work

https://github.com/generalkolo/Realm-Examples/tree/master/Realm%20Migration

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Thank you for your contribution.
While I liked the content of your contribution, I would still like to extend one advice for your upcoming contributions:

  • Screenshots: Provide better quality, clearer, and more professional screenshots.

Looking forward to your upcoming tutorials.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

·

Thanks @portugalcoin for taking out time to moderate my contribution.
Would improve my screenshot quality.

Hey @edetebenezer
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!