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:
- The table name is injected at runtime, replacing the
${TABLE_NAME}
placeholder - Backticks are wrapped around column names
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.