Realm Database on Android: Getting Started

Learn how to use Realm database on Android for data persistence. You’ll learn how to add it to your android app and utilize its features. By Rodrigo Guerrero.

5 (5) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Sorting Results

Sometimes you have to sort results from the queries before displaying them on the screen. Go to OwnerDatabaseOperations.kt and add the following line to the query in retrieveOwners() after .findAll():

.sort("name", Sort.ASCENDING)

Import io.realm.Sort.

This operation sorts the results by the name field in ascending order.

Build and run the app. Add other owners to the list. You’ll see the owners sorted by name, as in the following image:

Owners screen

Each owner displays the number of pets it owns, but so far, that number is always zero. How can you get the number of pets using Realm?

Making Calculations

Another value you need to display is the number of pets each owner has. Realm has different aggregate operators. These operators traverse a list of Realm objects and calculate a value.

Open OwnerDatabaseOperations.kt and add the following code:

private fun getPetCount(realm: Realm, ownerId: String): Long {
  // 1.
  return realm.where(PetRealm::class.java)
    // 2.
    .equalTo("owner.id", ownerId)
    // 3.
    .count()
}

In this code, you:

  1. Query the realm to get PetRealm objects.
  2. Use owner.id to filter by owner ID.
  3. Count the number of pets the owner has using .count()

Modify the Owner creation in retrieveOwners() to add numberOfPets, as follows:

Owner(
  name = owner.name,
  image = owner.image,
  id = owner.id,
  numberOfPets = getPetCount(realmTransaction, owner.id)
)

You now have pets up for adoption and owners. It’s time to let the owners adopt some pets.

Adding Relationships

Realm provides a way to implement one-to-one, one-to-many and inverse relationships.

Defining One-to-One Relationships

A one-to-one relationship is when an object relates at most with one instance of another object. In PetRealm, this happens with a pet that can have at most one owner, as shown in the following diagram:

PetRealm one-to-one relationship

Open PetRealm.kt and add the following parameter to the class constructor:

var owner: OwnerRealm? = null

This line tells the PetRealm object it can have at most one OwnerRealm. But there’s another relationship in this database: One owner can have multiple pets.

Defining One-to-Many Relationships

A one-to-many relationship is when one object relates to multiple objects. This is the scenario when a single owner can have multiple pets, as shown in the following diagram:

PetRealm one-to-many relationship

Open OwnerRealm.kt and add the following parameter to the class constructor:

var pets: RealmList<PetRealm> = RealmList()

Import io.realm.RealmList.

A RealmList allows the OwnerRealm to have a one-to-many relationship with PetRealm.

Good job! You have implemented two relationships in PetRealm. However, Realm provides a third type of relationship that can make your life easier.

Using Inverse Relationships

The relationships you implemented are unidirectional. This means when you get the query results for pets, their owners won’t be available in the results. This is a bit of a problem, because you have to show the pet owner in the adopted pets list.

Realm provides inverse relationships to solve this. Go to PetRealm.kt and replace the owner parameter in the constructor with the following lines:

@LinkingObjects("pets") // 1.
val owner: RealmResults<OwnerRealm>? = null // 2.

Import io.realm.annotations.LinkingObjects and io.realm.RealmResults.

To add an inverse relationship, you must:

  1. Add @LinkingObjects annotation, passing as parameter the name of the field you’re adding the relationship to. The field in OwnerRealm you want to link is pets.
  2. The field should be val and of type RealmResults.

With this improvement, you now can get the OwnerRealm information in the PetRealm query.

It’s time to build and run the app. Oh no! The app crashes. Taking a look at Logcat, you’ll find the following error:

Process: com.raywenderlich.android.petrealm, PID: 16049
	io.realm.exceptions.RealmMigrationNeededException: Migration is required due to the following errors:
	- Property 'OwnerRealm.pets' has been added.

After adding new fields, you have to create a migration to tell the database what has changed.

Migrating the Database

Realm maps each state of the database schema to a specific version. If this schema changes, like it did when you added the relationships in the previous section, the version needs to increment. You also have to tell Realm how to handle the differences in the schema by creating a migration.

Create a new file in the realm package and name it Migrations.kt. Add the following code:

// 1.
val migration = RealmMigration { realm, oldVersion, newVersion ->
  // 2.
  if (oldVersion == 1L) {
	// 3.
    val ownerSchema = realm.schema.get("OwnerRealm")
    val petSchema = realm.schema.get("PetRealm")

    // 4.
    petSchema?.let {
      ownerSchema?.addRealmListField("pets", it)
    }
  }
}

Import io.realm.RealmMigration.

To add a migration, you must:

  1. Create a val of type RealmMigration.
  2. Define what to do for each version change. oldVersion will hold the value for the previous schema version.
  3. Get each schema you need to modify.
  4. ownerSchema needs a new RealmList field of type PetRealm. Use addRealmListField() with the name of the field and the schema type the field needs.

Open PetsModule.kt in the di package. Increase the schema version like this:

private val realmVersion = 2L

Modify providesRealmConfig() as follows:

RealmConfiguration.Builder()
  .schemaVersion(realmVersion)
  .migration(migration)
  .build()

Use migration() to add the migration. If you build and run the app, it runs again. Everything’s working as expected. It’s time for the most important step: adopting pets.

Updating Objects

To update Realm objects, you need to query for the object you want to update and assign the new values.

To adopt a pet, isAdopted should change to true and the pet gets assigned to the owner. Open OwnerDatabaseOperations.kt and modify updatePets() like this:

suspend fun updatePets(petId: String, ownerId: String) {
  val realm = Realm.getInstance(config)

  // 1.
  realm.executeTransactionAwait(Dispatchers.IO) { realmTransaction ->
    // 2.
    val pet = realmTransaction
      .where(PetRealm::class.java)
      .equalTo("id", petId)
      .findFirst()

    // 3.
    val owner = realmTransaction
      .where(OwnerRealm::class.java)
      .equalTo("id", ownerId)
      .findFirst()

    // 4.
    pet?.isAdopted = true
    // 5.
    owner?.pets?.add(pet)
  }
}

In this code, you:

  1. Add a transaction to run the write operation.
  2. Query for the lucky pet.
  3. Query for the owner who will adopt the pet.
  4. Update isAdopted value.
  5. Add the pet to the owner’s pet list.

Open PetDatabaseOperations.kt and modify mapPet() as follows:

private fun mapPet(pet: PetRealm): Pet {
  return Pet(
    name = pet.name,
    age = pet.age,
    image = pet.image,
    petType = pet.petType,
    isAdopted = pet.isAdopted,
    id = pet.id,
    ownerName = pet.owner?.firstOrNull()?.name
  )
}

You can take advantage of the relationships you added previously and add the owner’s name to each adopted pet.

Build and run the app. Press Adopt Me in any pet and select an owner. Make a single owner to adopt several pets. Navigate to the Adopted Pets screen and you’ll see the lucky pets:

Adopted pets screen

Open the Owners screen. You can see the number of pets each owner adopted.

Owners with pets screen

Great job! You helped the pets find an owner. However, there could be times when a pet runs away or an owner is no longer interested in adopting. In this case, you need to be able to remove them from the app.