Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. Because Room takes care of several concerns for you, e.g. caching, networking, Android highly recommend using Room instead of SQLite. If you are coming from iOS, the good news is that Room is very similar to the core data in iOS development.
The task
Field name
Type
uid
Integer
name
String
photo
Bitmap
dob
LocalDate
User table
We will create a user data model, similar to the one from Android documentation website. Nevertheless, we will add more complicated data to the model as well as some queries.
The user model will look like this.
Getting started
To use Room in your app, add the following dependencies to your app’s build.gradle file:KOTLINJAVA
First you will need to add kapt to your project build.gradle:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
Secondly, you will add the room’s dependencies.
dependencies {
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:room_version"
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:room_version"
}
Sync your project before creating the data model.
Implement data model
With Room, we use all annotation, e.g. @Entity to denote the class as a data entity (table), @PrimaryKey for the primary key. The Room framework will handle the table creation for you.
@Entity
data class User(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "name") val name: String?,
@ColumnInfo(name = "dob") val dob: LocalDateTime?,
@ColumnInfo(name = "photo") val photo: Bitmap? = null
)
data access objects (dao)
To access the table created above, we need to declare a DAO class. The following code provides the standard methods in the database, i.e. insert, delete, select. Similarly, we use @Query to define the custom query that we want to assign to the method, use @Delete to create standard delete user.
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getAll(): List<User>
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
fun loadAllByIds(userIds: IntArray): List<User>
@Query("SELECT * FROM user WHERE name LIKE :name LIMIT 1")
fun findByName(name: String): User
@Insert
fun insertAll(vararg users: User)
@Delete
fun delete(user: User)
}
Database
The database to store these tables can be defined as follow
@Database(entities = arrayOf(User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Address the uncommon data types
Now if you try to compile your app, you will have the following error
error: Cannot figure out how to save this field into database.
Execution failed for task ':app:kaptDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
> java.lang.reflect.InvocationTargetException (no error message)
It is because there are data types, e.g. Bitmap and LocalDate that Room does not know how to store it automatically. To solve this, we need to provide relevant methods for retrieve and save these data. We use @TypeConverter for these methods.
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): LocalDateTime? {
return value?.let {
Instant.ofEpochMilli(it)
.atZone(ZoneId.of("UTC")).toLocalDateTime()
}
}
@TypeConverter
fun dateToTimestamp(date: LocalDateTime?): Long? {
return date?.atOffset(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
}
@TypeConverter
fun fromByteArray(bitmapdata: ByteArray?): Bitmap? {
return bitmapdata?.let {
BitmapFactory.decodeByteArray(bitmapdata, 0, bitmapdata.size)
}
}
@TypeConverter
fun dateToByteArray(bmp: Bitmap?): ByteArray? {
val stream = ByteArrayOutputStream()
bmp?.compress(Bitmap.CompressFormat.PNG, 100, stream)
val byteArray: ByteArray = stream.toByteArray()
return byteArray
}
}
Now we can update the Appdatabase with the annotataion @TypeConverters as follow
@Database(entities = arrayOf(User::class), version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
It should compile normally.
Define relations
If you have another table, e.g. Library and you want to set user-library relations
@Entity
data class Library(
@PrimaryKey val libraryId: Long,
val userOwnerId: Long
)
To do this, create a new data class where each instance holds an instance of the parent entity and the corresponding instance of the child entity. Add the @Relation annotation to the instance of the child entity, with parentColumn set to the name of the primary key column of the parent entity and entityColumn set to the name of the column of the child entity that references the parent entity’s primary key.
data class UserAndLibrary(
@Embedded val user: User,
@Relation(
parentColumn = "userId",
entityColumn = "userOwnerId"
)
val library: Library
)
Tips for Some other problems
If your project failed to build, it is likely that you have issue with Dao class. Check it thoroughly.
If you have problem with schema, you can add to defaultConfig of build.gradle
PetaMinds focuses on developing the coolest topics in data science, A.I, and programming, and make them so digestible for everyone to learn and create amazing applications in a short time.