But First, a Word About Exporting Schemas

One of the side-effects of using Room is that you do not write your own schema for the database. Room generates it, based on your entity definitions. During the ordinary course of programming, this is perfectly fine and saves you time and effort.

However, when it comes to migrations, now we have a problem. We cannot create code to migrate from an old to a new schema without knowing what those schemas are. And while schema information is baked into some code generated by Room’s annotation processor, that is only for the current version of your entity classes (and, hence, your current schema), not for any historical ones.

Fortunately, Room offers something that helps a bit: exported schemas. You can teach Room’s annotation processor to not only generate Java code but also generate a JSON document describing the schema. Moreover, it will do that for each schema version, saving them to version-specific JSON files. If you hold onto these files — for example, if you save them in Git or some other form of version control — you will have a history of your schema and can use that information to write your migrations.

However, the real reason for those exported schemas is to help with testing your migrations. As a result, the JSON format is not designed for developers to read.

To set this up, in the defaultConfig closure of your module’s build.gradle file, you can add a javaCompileOptions closure:

  defaultConfig {
    minSdkVersion 21
    targetSdkVersion 30
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

    javaCompileOptions {
      annotationProcessorOptions {
        arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
      }
    }
  }

This teaches Room to save your schemas in a schemas/ directory off of the module root directory. In principle, you could store them elsewhere by choosing a different value for the room.schemaLocation argument.

The next time you (re-)build your project, that directory will be created. Subdirectories with the fully-qualified class names of your RoomDatabase classes will go inside there, and inside each of those will be a JSON file named after your schema version (e.g., 1.json):

{
  "formatVersion": 1,
  "database": {
    "version": 1,
    "identityHash": "051fc3ca1ecb3344055fd77365a9bf8e",
    "entities": [
      {
        "tableName": "notes",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `text` TEXT NOT NULL, `version` INTEGER NOT NULL, PRIMARY KEY(`id`))",
        "fields": [
          {
            "fieldPath": "id",
            "columnName": "id",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "title",
            "columnName": "title",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "text",
            "columnName": "text",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "version",
            "columnName": "version",
            "affinity": "INTEGER",
            "notNull": true
          }
        ],
        "primaryKey": {
          "columnNames": [
            "id"
          ],
          "autoGenerate": false
        },
        "indices": [],
        "foreignKeys": []
      }
    ],
    "views": [],
    "setupQueries": [
      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '051fc3ca1ecb3344055fd77365a9bf8e')"
    ]
  }
}

The JSON properties that will matter to you will be the createSql ones. There are ones that create your tables and others that create your indexes. This is fairly normal SQL, except that:


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.