Kotlin Android Room Reference Guide
Core Annotations
@Entity
Marks a class as a database table
```kotlin
@Entity(tableName = "users")
data class User(
@PrimaryKey val id: Int,
@ColumnInfo(name = "first_name") val firstName: String,
@ColumnInfo(name = "last_name") val lastName: String
)
```
@Dao
Marks an interface as a Data Access Object
```kotlin
@Dao
interface UserDao {
// DAO methods go here
}
```
@Database
Marks a class as a Room database
```kotlin
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
```
Common DAO Methods
Query Operations
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAll(): List<User>
@Query("SELECT * FROM users WHERE id = :userId")
fun getById(userId: Int): User?
@Query("SELECT * FROM users WHERE first_name LIKE :search")
fun searchByName(search: String): List<User>
}
```
Modification Operations
@Dao
interface UserDao {
@Insert
fun insert(user: User)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrUpdate(user: User)
@Update
fun update(user: User)
@Delete
fun delete(user: User)
}
```
Suspend Functions for Coroutines
```kotlin
@Dao
interface UserDao {
@Query("SELECT * FROM users")
suspend fun getAllSuspend(): List<User>
@Insert
suspend fun insertSuspend(user: User)
}
```
Flow Returns for Reactive Programming
In Android/Kotlin, a Flow is a way to handle streams of data that can change over time. When used with Room, it means your app can automatically react to database changes. Here's a practical example:
Let's say you have a screen showing a list of users. Without Flow:
// Without Flow - you'd need to manually refresh
val users = userDao.getAll() // Returns List<User>
// Data becomes stale if database changes
With Flow:
// With Flow - automatically updates
val users = userDao.getAllAsFlow() // Returns Flow<List<User>>
// Now in your Activity/Fragment:
lifecycleScope.launch {
users.collect { latestUsersList ->
// This block runs automatically whenever the users table changes
updateUI(latestUsersList)
}
}
The key benefits are:
Real-time updates - your UI automatically reflects database changes Memory efficiency - Flow only emits when there are actual changes Lifecycle awareness - when used with coroutines, it respects Android lifecycle A real-world example would be a chat app:
@Dao
interface MessageDao {
@Query("SELECT * FROM messages WHERE chatId = :chatId ORDER BY timestamp DESC")
fun getMessagesForChat(chatId: String): Flow<List<Message>>
}
// In your ChatActivity:
viewModelScope.launch {
messageDao.getMessagesForChat(currentChatId)
.collect { messages ->
// UI updates automatically when new messages arrive
messageAdapter.submitList(messages)
}
}
This means when a new message arrives and is inserted into the database, the UI updates automatically - you don't need to write code to refresh or poll for changes.
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAllAsFlow(): Flow<List<User>>
@Query("SELECT * FROM users WHERE id = :userId")
fun getByIdAsFlow(userId: Int): Flow<User?>
}
```
Database Builder
```kotlin
// Building the database instance
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"database-name"
)
.fallbackToDestructiveMigration() // Optional: Recreates DB if no migration found
.build()
```
Type Converters
```kotlin
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): Date? {
return value?.let { Date(it) }
}
@TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time
}
}
// Add to database
@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase()
```
Common Relationships
@Relation One-to-Many
```kotlin
data class UserWithPosts(
@Embedded val user: User,
@Relation(
parentColumn = "id",
entityColumn = "userId"
)
val posts: List<Post>
)
```
@Junction Many-to-Many
```kotlin
@Entity(primaryKeys = ["userId", "tagId"])
data class UserTagCrossRef(
val userId: Int,
val tagId: Int
)
data class UserWithTags(
@Embedded val user: User,
@Relation(
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(UserTagCrossRef::class)
)
val tags: List<Tag>
)
```
Migration (from one database schema to another)
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"ALTER TABLE users ADD COLUMN age INTEGER"
)
}
}
// Add migration to database builder
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"database-name"
)
.addMigrations(MIGRATION_1_2)
.build()
```