Room Furnishings
Roughly speaking, your use of Room is dominated by three sets of classes:
- Entities, which are simple classes that model the data you are transferring into and out of the database
- The data access object (DAO), that provides the description of the API that you want for working with certain entities
- The database, which ties together all of the entities and DAOs for a single SQLite database
If you have used Square’s Retrofit, some of this will seem familiar:
- The DAO is roughly analogous to your Retrofit
interface
on which you declare your Web service API - Your entities are the POJOs that you are expecting Gson/Moshi/whatever to create based on the Web service response
The NoteBasics
module mentioned above has a few classes related to a note-taking application, exercised via instrumented tests.
Entities
In many ORM systems, the entity (or that system’s equivalent) is a simple class that you happen to want to store in the database. It usually represents some part of your overall domain model, so a payroll system might have entities representing departments, employees, and paychecks.
With Room, a better description of entities is that they are classes representing:
- the data that you want to store into a table, and
- a typical unit of a result set that you are trying to retrieve from the database
That difference may sound academic. It starts to come into play a bit more when we start thinking about relations.
However, it also more closely matches the way Retrofit maps to Web services. With Retrofit, we are not describing the contents of the Web service’s database. Rather, we are describing how we want to work with defined Web service endpoints. Those endpoints have a particular set of content that we can work with, courtesy of whoever developed the Web service. We are simply mapping those to methods and classes, both for input and output. Room is somewhere in between a Retrofit-style “we just take what the Web service gives us” approach and a full ORM-style “we control everything about the database” approach.
From a coding standpoint, an entity is a Java/Kotlin class marked with the @Entity
annotation. For example, here is a NoteEntity
class that serves as a Room entity:
package com.commonsware.room.notes
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "notes")
data class NoteEntity(
@PrimaryKey val id: String,
val title: String,
val text: String,
val version: Int
)
There is no particular superclass required for entities, and the expectation is that often they will be simple data
classes, as we see here.
The @Entity
annotation can have properties customizing the behavior of your entity and how Room works with it. In this case, we have a tableName
property. The default name of the SQLite table is the same as the entity class name, but tableName
allows you to override that and supply your own table name. Here, we override the table name to be notes
.
Sometimes, your properties will be marked with annotations describing their roles. In this example, the id
field has the @PrimaryKey
annotation, telling Room that this is the unique identifier for this entity. Room will use that to know how to update and delete Note
objects by their primary key values. In Java, Room also requires that any @PrimaryKey
field of an object type — like String
— be annotated with @NonNull
, as primary keys in SQLite cannot be null
. In Kotlin, you can just use a non-nullable type, such as String
.
We will explore entities in greater detail in an upcoming chapter.
DAO
“Data access object” (DAO) is a fancy way of saying “the API into the data”. The idea is that you have a DAO that provides methods for the database operations that you need: queries, inserts, updates, deletes, and so on.
In Room, the DAO is identified by the @Dao
annotation, applied to either an abstract
class or an interface
. The actual concrete implementation will be code-generated for you by the Room annotation processor.
The primary role of the @Dao
-annotated abstract
class or interface
is to have one or more methods, with their own Room annotations, identifying what you want to do with the database and your entities. This serves the same role as the functions annotated @GET
or @POST
in a Retrofit interface.
The sample app has a NoteStore
that is our DAO:
package com.commonsware.room.notes
import androidx.room.*
@Dao
interface NoteStore {
@Query("SELECT * FROM notes")
fun loadAll(): List<NoteEntity>
@Insert
fun insert(note: NoteEntity)
@Update
fun update(note: NoteEntity)
@Delete
fun delete(vararg notes: NoteEntity)
}
Besides the @Dao
annotation on the NoteStore
interface, we have four functions, each with their own annotations: @Query
, @Insert
, @Update
, and @Delete
, each which map to the corresponding database operations.
The loadAll()
function has the @Query
annotation. Principally, @Query
will be used for SQL SELECT
statements, where you put the actual SQL in the annotation itself. Here, we are retrieving everything from the notes
table.
The remaining three functions use the @Insert
, @Update
, and @Delete
annotations, mapped to functions of the same name. The actual function names do not matter — they could be larry()
, curly()
, and moe()
and work just as well. As you might expect, @Insert
inserts an entity into our table, @Update
updates an existing table row to reflect the supplied entity’s properties, and @Delete
deletes table rows corresponding with the supplied entities’ primary keys. In this sample, insert()
and update()
each take a single NoteEntity
, while delete()
takes a vararg
of NoteEntity
. Room supports either pattern, as well as others, such as a List
of NoteEntity
— choose what fits your needs.
We will explore the DAO in greater detail in an upcoming chapter.
Database
In addition to entities and DAOs, you will have at least one @Database
-annotated abstract
class, extending a RoomDatabase
base class. This class knits together the database file, the entities, and the DAOs.
In the sample project, we have a NoteDatabase
serving this role:
package com.commonsware.room.notes
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(entities = [NoteEntity::class], version = 1)
abstract class NoteDatabase : RoomDatabase() {
abstract fun notes(): NoteStore
}
The @Database
annotation configures the code generation process, including:
- Identifying all of the entity classes that you care about in the
entities
collection - Identifying the schema version of the database (as you see with
SQLiteOpenHelper
in conventional Android SQLite development)
Here, we are saying that we have just one entity class (NoteEntity
), and that this is schema version 1.
You also need abstract
functions for each DAO class that return an instance of that class. Here, we have a notes()
function that returns NoteStore
.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.