# Room Persistence Library Introduction — part 1

*This article is the first part of the three-part series that will smoothly introduce Room Persistence Library to you. The first part will be focused on configuring the project and explaining the basic structures.* All sources can be found in related [***GitHub***](https://github.com/mkonkel/RoomBeginners) project.

# **What is Room?**

The Room is a persistence library that provides an object-mapping abstraction layer over SQLite to more robust database access while harnessing the full power of SQLite. It comes along with the Architecture components and was presented at Google I/O in 2017. Now it has reached version 2.0 and it is a part of Android Jetpack (on 10.10.2018 the 2.1 alpha 1 version was released). In this article, we will focus on version 1.1.1.

# **Let’s start!**

At the beginning we need to create an Android Project with the Kotlin support, API 27 (or greater) with “No Activity” option. To use Room we need to add some dependencies to our gradle file.

```plaintext
dependencies {
    def room_version = "1.1.1"

    implementation "android.arch.persistence.room:runtime:$room_version"
    kapt "android.arch.persistence.room:compiler:$room_version"

   ...
}
```

## **Basic elements**

Room consists of a few basic elements that you should know before starting any work.

## `@Entity`

A class annotated with `@Entity` will represent our column in the database. Basically, it is just a POJO set of related fields.

By default, Room creates a column for each field that’s defined in the entity. If an entity has fields that you don’t want to persist, you can annotate them using `@Ignore` annotation.  
To persist a field, Room must have access to it. You have to make a field public, or at least provide a getter and setter for it.

The tricky part with `@Ignore` and Kotlin “behind the scenes” generation of constructors for nullable fields. In that particular case, Kotlin will generate every possible constructor for class with nullable values — this will cause an error — because Room needs an empty constructor. One way is to override constructors as described in the kotlinsdocumentation. The Second one (the easiest one) is to set default values for the fields.

## `@PrimaryKey`

Each entity must define at least one field as a primary key. Even when there is only one field, you still need to annotate the field with the `@PrimaryKey` annotation. In addition, if you want Room to assign automatic IDs to entities, you should set the `@PrimaryKeyautoGenerate` property to true. You can also provide a composite primary key, just use the ***primaryKeys*** property of the `@Entity.`

By default, Room uses the class name as the database table name. If you want the table to have a different name, set the ***tableName*** property of the `@Entity` annotation. Similarly to the ***tableName*** property, Room uses the field names as the column names in the database. If you want a column to have a different name, add the `@ColumnInfo` annotation to a field.

Let’s write some code!

```kotlin
@Entity(tableName = "users")
data class User(
        @PrimaryKey(autoGenerate = true)
        var id: Long,

        var firstName: String,

        var lastName: String,

        var fullName: String,

        @ColumnInfo(name = "email")
        var emailAddress: String,

        @ColumnInfo(name = "phone")
        var phoneNumber: String,

        var picture: String
)
```

What we’ve got here is a simple user entity whose name will be users, primary key will be autogenerated long. The code above will generate a corresponding table in the database.

## `@Dao`

To access your app’s data using the Room persistence library, you should work with data access objects or DAOs.  
This set of DAO objects forms the main component of Room, as each DAO includes methods that offer abstract access to your app’s database.  
Basically, this is the point where you will be communicating with the database — here you will be defining your data interactions, mapping SQL queries to functions and more.

It is recommended to have multiple Dao classes in your codebase depending on the tables they touch. Room creates each DAO implementation at compile time.

DAO consists of 4 major methods `@Insert, @Update, @Delete` and `@Query`

## `@Insert`

The implementation of the method will insert its parameters into the database.  
If the `@Insert` method receives only one parameter, it can return a long, which is the new ***row\_id*** for the inserted item. If the parameter is an array or a collection, it should return long\[\] or List&lt;Long&gt; instead.  
`@Insert` contains the ***onConflict*** property which determines the SQLite conflict resolving strategy when inserting data.

## `@Update`

The implementation of the method will update its parameters in the database if they already exist (checked by primary keys). If they don’t already exist, this option will not change the database.

## `@Delete`

The implementation of the method will delete its parameters from the database. It uses the primary keys to find the entities to delete.

## `@Query`

The main annotation used in DAO classes allows you to perform read/write operations on a database. The query is verified at compile time by Room to ensure that it compiles fine against the database.

Let’s write some code!

```kotlin
@Dao
interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUser(user: User)

    @Insert
    fun insertUsers(users: List&lt;User&gt;)

    @Update
    fun updateUser(user: User)

    @Update
    fun updateUsers(vararg users: User)

    @Delete
    fun deleteUser(user: User)

    @Delete
    fun deleteUsers(users: List&lt;User&gt;)

    @Query("SELECT * FROM users")
    fun users(): List&lt;User&gt;
}
```

As you can see we have some basic methods that allow us to insert, update or delete a single user or a list of users. The methods mentioned above use the primary key to determine which row should be affected.

You can also pass parameters into queries to perform filtering operations, such as only displaying users with a certain name.

Room only supports named bind parameter to avoid any confusion between the method parameters and the query bind parameters.  
Room will automatically bind the parameters of the method into the bind arguments. This is done by matching the name of the parameters to the name of the bind arguments.

```kotlin
@Query("SELECT * FROM users WHERE firstName = :userName")
fun usersWithName(userName: String): List&lt;User
```

We will focus on this a little bit more in the second part of the article series.

# `@Database`

Contains the database holder and serves as the main access point for the underlying connection to your app’s persisted, relational data. A class annotated with `@Database` should be an abstract class and extend **RoomDatabase**. You can receive an implementation of the class via ***Room.databaseBuilder***.

**RoomDatabase** provides direct access to the underlying database implementation but you should prefer using Dao classes.

Database class should consist at least of:

* List of entities
    
* DB version
    
* Abstract methods returning DAO’s
    
* Database builder method.
    

Let’s code to see this in action!

```kotlin
@Database(
        entities = [User::class],
        version = AppDatabase.DB_VERSION
)
abstract class AppDatabase : RoomDatabase() {

    abstract fun userDao(): UserDao

    companion object {
        const val DB_VERSION = 1
        const val DB_NAME = "application.db"

        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase =
                INSTANCE ?: synchronized(this) {
                    INSTANCE ?: buildDatabase(context)
                }

        private fun buildDatabase(context: Context) =
                Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, DB_NAME)
                        .build()
    }
}
```

From the beginning:  
Annotation `@Database` requires two properties:

* List of entities — the tables in our DB (classes)
    
* DB version
    

At the top of the class, we should declare all DAO’s as the abstract functions — it’s just a convention.  
Then, we should always use the singleton pattern to obtain the database object, because the creation of the DB connection is quite expensive.  
The code above will create room database when anything asks for its instance.

We are also using:

* `@Volatile` — that has semantics for memory visibility. Basically, the value of a volatile field becomes visible to all readers (other threads in particular) after a write operation completes on it. Without volatile, readers could see some non-updated value.
    
* `synchronized(this)` — in the simplest words, when you have two threads that are reading and writing to the same ‘resource’ you need to ensure that these threads access the variable in an atomic way. Without it, the thread 1 may not see the change thread 2 made to a variable.
    

Furthermore, it’s also quite handy to get some Injector class that will provide necessary objects that we will later use in our Activity.

```kotlin
object Injector {
    fun provideUserDao(context: Context): UserDao {
        return AppDatabase.getInstance(context).userDao()
    }
}
```

Another thing we should take care of is **pre-populating** our database in some data set. With the traditional approach the data will probably come from some web API — for this short tutorial, we will use prepared data.

For this task, we will add the **onCreate** callback to the **databaseBuilder**. We should keep in mind that any operation related with the DB cannot be performed on the **mainThread** — because Room will throw an exception — so kotlin coroutine sounds like a good plan for this task.

We will need to add some dependencies to our app gradle.

```plaintext
kotlin {
    experimental {
        coroutines "enable"
    }
}
…
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.26.1'
```

Then we also need to add some code to the database builder.

```kotlin
private fun buildDatabase(context: Context) =
        Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, DB_NAME)
                .addCallback(dbCreateCallback(context))
                .build()

private fun dbCreateCallback(context: Context) = object : Callback() {
     override fun onCreate(db: SupportSQLiteDatabase) {
         super.onCreate(db)
         GlobalScope.launch {
            getInstance(context).userDao()
                    .insertUsers(PrepopulateData.users)
        }
     }
}
```

Our prefilled data can look like that:

```kotlin
object PrepopulateData {
    val users = listOf(
            User(
                    id = null,
                    firstName = "John",
                    lastName = "Doe",
                    fullName = "John Doe",
                    emailAddress = "jdoe@mail.com",
                    phoneNumber = "001333444555",
                    picture = "/pictures/jdoe/avatar/s34trag_732_jkdal.png"
            ),
            User(
                    id = null,
                    firstName = "Mark",
                    lastName = "Smith",
                    fullName = "Mark Smith",
                    emailAddress = "mastermike@mail.com",
                    phoneNumber = "001666999888",
                    picture = "/pictures/msmith/avatar/123454647_gfas.png"
            )
    )
}
```

The given changes will create two new users when **DB** is first created, this will allow us to pre-populate **DB** when it’s first created. Now there is nothing more left for us, let’s finally check if everything works.

We can add some simple activity to validate the code.

```kotlin
class TestActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)

        val userDao = Injector.provideUserDao(this)

        GlobalScope.launch {
            val users = userDao.users()

            users.forEach {
                Log.d("User", “$it")
            }
        }
    }
}
```

Now if you run the application and everything went OK and the is showing a blank screen you should see a log message similar to this

```kotlin
User(id=1, firstName=John, lastName=Doe, fullName=John Doe, emailAddress=jdoe@mail.com, phoneNumber=001333444555, picture=/pictures/jdoe/avatar/s34trag_732_jkdal.png)
User(id=2, firstName=Mark, lastName=Smith, fullName=Mark Smith, emailAddress=mastermike@mail.com, phoneNumber=001666999888, picture=/pictures/msmith/avatar/123454647_gfas.png
```

That’s All Folks! We’ve reached the end of the first part of the introduction to the **Room Persistence Library**. I hope you’ve enjoyed the post and you can’t wait for more.  
The second part will cover some details of `@Entities`**,** we will learn how to use TypeConverters and Embedded Entities.

**Cheers!**

> *This post was originally published on*[*Speednet blog 16.10.201*](https://speednet.pl/blog/room-persistence-library-introduction-part-1/)*8*
