I’d like to begin with a short preface about me. I am an Android developer for almost 3 years and during that time I architected database layer literally 0 times.

In my very first project there were no need for a database. We had a short term persistence layer made with SharedPreferences. One reason of it was that we needed to store 20 last searches and it was fine if we lose data on migration. We actually never lose data because it was very simple. Another reason was that I did not know how to work with databases in Android. Then I had a bigger project where persistence layer already existed and it never changed. Then I went back to school and by that time I still haven’t touched databases.

Now I’m working on a fairly big app and to be honest I still feel like I am a database noob. Yes, I can change a model and write a migration. I can access database and get all the data I need but I still feel that my top achievement in architecting databases was Chapter 14 from Android Programming: The Big Nerd Ranch Guide (3rd Edition)(highly recommend that book, by the way).

When release of Room I decided to take a chance and finally learn some basics and also write that article to practice writing skills.

All code will be written in Kotlin (another thing I want to practice) and will be available at https://github.com/iwuvhugs/Choice. Please feel free to PR any of your ideas of how to improve the code, style or maybe something else.

First of all you need to add dependencies in the gradle file:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
...
defaultConfig {
...

javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
}
}
dependencies {
implementation 'android.arch.persistence.room:runtime:1.0.0-alpha8'
kapt 'android.arch.persistence.room:compiler:1.0.0-alpha8'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-alpha8'
}

Note: in Kotlin you use when kotlin-kapt plugin added

kapt 'android.arch.persistence.room:compiler:1.0.0-alpha8'

in Java you use

annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-alpha8'

Those are the same things as far as I understand. Also at the moment I wrote the article last version of Room was 1.0.0-alpha8. You will need to bump it up with new versions.

Also note: you can skip adding this

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

but Android Studio will complain. When you add this snippet, Room will create DB schemas in that folder and I personally find it’s super helpful. This is how it will look in the project:

Next, you can start creating entities (i.e. tables)

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

@Entity(tableName = "question")
data class Question(
@PrimaryKey(autoGenerate = true)
val id: Long,
@ColumnInfo(name = "title")
val title: String,
@ColumnInfo(name = "description")
val description: String
)

Here you need to mark your class with @Entity annotation so it will have a mapping SQLite table in the database. You can explicitly set table name using tableName. Without it table will be name default to class name. Then each entity must have at least 1 primary key marked with PrimaryKey annotation (or you could specify it with @Entity(primaryKeys())). Then you could use @ColumnInfo if you want to keep snake case naming for table columns and camel case for Java/Kotlin variable names.

Second part here is Kotlin’s data class. That class is specifically created to hold data so it makes it a perfect candidate for entities. Each data class must have a primary constructor with at least one parameter. One cool thing here: when specify parameters val is used to create getters only, var creates both getters and setters for properties. Another thing: if you have you primary key autogenerated by sql you don’t really need to pass id as argument every time. To fide id from being passed into constructor you can create a secondary constructor like this (but you’ll need to hide it from Room with @Ignore ):

@Ignore
constructor(title: String= "", description: String = "") : this(0, title, description)

Third part is to create DAO. Don’t have many things to write about here.

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

@Dao
interface QuestionDao {

@Query("SELECT * FROM question")
fun getQuestions(): List<Question>

@Insert
fun insert(question: Question)
}

In DAO you define all the operations you want to perform over you data. I have @Insert and @Query here. In query you need to write SQL. It might seem weird because you can avoid writing SQL queries with ORM libraries, but in case of Room this is fine.

If you write wrong query, Room will prevent app from being compiled. You could check which exact query is wrong in logs and fix it. Room will cover your back 👍

Next you’ll need to create class for database:

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

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

You need to extend your class from RoomDatabase and also list tables and DAO classes that are used to access it. Also you’ll need to specify DB version here. Room will create a schema in the folder you’ve listed in gradle. It’s kinda cool because it will create a new schema for every new version. If you’re not super fluent in SQL (like me) you can use that schemas to write migrations or queries.

Last step, you’ll need to initialize created database.

class ChoiceApplication : Application() {

companion object {
var db: ChoiceDatabase? = null
}

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

I kept things simple and created database in application class. Maybe later I later I will find a fancier way.

Room prevents from accessing database from the main thread. It will crash (at runtime if I’m not mistaken). There’s bunch of way to do things asynchronously. I have a simple example here using thread (I get list of all questions here and set them into adapter to display a list).

val handler = Handler()
Thread({
val questions = ChoiceApplication.db?.questionDao()?.getQuestions()
handler.post({
if (questions != null) {
questionListAdapter!!.addQuestions(questions)
}
})
}).start()

Another example: insert new question (using secondary constructor here)

val question = Question(title = title, description = description)
Thread({
ChoiceApplication.db?.questionDao()?.insert(question)
}).start()

You can find sample app here https://github.com/iwuvhugs/Choice

Thanks for reading

Shoutout to sources I used:

Tony Owen (thanks!)
https://medium.com/@tonyowen/a-room-with-a-view-getting-started-ec010f9f5448
https://medium.com/@tonyowen/room-entity-annotations-379150e1ca82

https://developer.android.com/topic/libraries/architecture/room.html

UPD: I wrote second part where I add second table to the app and write a migration. Check it out too:

Mobile developer, snowboarder, dog lover

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store