If you haven’t read my previous post about setting up the Room I highly recommend to check it out first. Also repo with examples is https://github.com/iwuvhugs/Choice

If you’ve done steps from that article you have app with one table and couple queries aka classic-new-library-tutorial-app. However in real life you probably need more than one entity in your app.

Assume you already have app with a single questions table. Let’s add second table for answers . questions and answers will have one-to-many relation.

import android.arch.persistence.room.ColumnInfo
import android.arch.persistence.room.Entity
import android.arch.persistence.room.ForeignKey
import android.arch.persistence.room.PrimaryKey

@Entity(tableName = "answer",
foreignKeys = arrayOf(ForeignKey(entity = Question::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("question_id"),
onDelete = ForeignKey.CASCADE)))
data class Answer(
@PrimaryKey(autoGenerate = true)
val id: Long,
@ColumnInfo(name = "question_id")
val questionId: Long,
@ColumnInfo(name = "answer")
val answer: String)

Like before, you start with data class that represent your entity or table in the database. New thing in @Entity is foreignKeys . Foreign keys allow you to specify constraints across Entities. foreignKeys can accept multiple keys, in my example I have only one. ForeignKey accepts class name or entity which the table should have a relation with, parentColumns which specify keys in the parent table (could be several keys - I have just one in the example), childColumns which specify keys in the current table (could be several keys as well). In addition you could add onDelete and onUpdate actions that will be triggered when parent table is deleted or update. In my example I do cascading i.e. when question is deleted all its answers will be deleted too.

Then you need to create DAO for answers table. Pretty much the same thing as questions.

import android.arch.persistence.room.Dao
import android.arch.persistence.room.Insert
import android.arch.persistence.room.Query

@Dao
interface AnswerDao {

@Query("SELECT * FROM answer")
fun getAnswers(): List<Answer>

@Insert
fun insert(answer: Answer)
}

I have two database operations here just as an example.

Then you need to add entity and DAO into you database class:

import android.arch.persistence.room.Database
import android.arch.persistence.room.RoomDatabase

@Database(entities = arrayOf(
Question::class,
Answer::class),
version = 2)
abstract class ChoiceDatabase : RoomDatabase() {
abstract fun questionDao(): QuestionDao
abstract fun answerDao(): AnswerDao
}

Also you’ll need to bump you database version. You can architect you database in one step but in real life you will likely need to add new tables with new release so you’ll need to migrate the database.

If you try to run app at this point you will likely get a crash that looks like:

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you’ve changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
at android.arch.persistence.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:118)
at android.arch.persistence.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:99)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$1.onOpen(FrameworkSQLiteOpenHelper.java:64)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:266)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:106)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:82)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:191)
at com.iwuvhugs.choice.dao.QuestionDao_Impl.getQuestions(QuestionDao_Impl.java:59)
at com.iwuvhugs.choice.question.list.QuestionListFragment$onResume$1.run(QuestionListFragment.kt:34)
at java.lang.Thread.run(Thread.java:761)

This is a very cool feature of Room. If schema was changed you will find it really quick. Moreover app won’t compile until you fix it. To fix it you need to write a migration.

Now I probably teach you some bad development practices but nobody told me that this is bad so ¯\_(ツ)_/¯. If your SQL skill are weak (like mine haha) Room gives you some hints for migrations. When you try to run the app and get the crash, Room still generates new schema. You can open new version of schema and compare the diff with previous version. I personally find it helpful. In my example new entity was added (obvious but also schema.v2 shows that 😉) so the migration should have a query to create this table. I just found the query in the schema for creating answer table and copy pasted it in the migration.

val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `answer` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `question_id` INTEGER NOT NULL, `answer` TEXT NOT NULL, FOREIGN KEY(`question_id`) REFERENCES `question`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
}
}

Good practice here will be to extract table and column name as constants but I might do it later. Also in the article it seems like more clear to read.

Then you need to add that migration when initialize you database. In my example it was in application class so the code will be:

override fun onCreate() {
super.onCreate()
db = Room.databaseBuilder(this, ChoiceDatabase::class.java, "choice_db")
.addMigrations(MIGRATION_1_2).build()
}

addMigrations() accepts vararg so you just list all your migrations here. Or create a util class that is responsible for organizing all migrations.

Now if everything is done correct app should compile successfully. Congrats, you have two tables now.

P.S. It’s not easy to adapt new library. When I decide to learn something new I never read documentation first. I mostly google <library name> example or tutorial. Very often I end up reading Medium article and then following steps from it. Sometimes it’s frustrating when an article ends quick showcasing only hello world kind of examples. I have couple more ideas for another article (maybe couple) about Room but if you have any ideas of helpful things to document, please leave comments, and then I could totally try in in the app and share some results in another article.

Kirill Suslov

Written by

Mobile developer, snowboarder, dog lover

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade