4. Integrate Room database with entities, DAO, and repository

Daniyar Nurgaliyev
2 min readJun 21, 2024

--

1. Defining the Entity

An entity in Room represents a table within the database. In this example, we create a User entity to store user information.

…/data/User.kt

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "user_table")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String
)

2. Creating the DAO

DAO (Data Access Object) is an interface that defines the database operations. It provides methods to perform CRUD (Create, Read, Update, Delete) operations on the database.

…/data/UserDao.kt

@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(user: User)

@Query("SELECT * FROM user_table ORDER BY name ASC")
fun getUsers(): Flow<List<User>>

@Update
suspend fun update(user: User)

@Delete
suspend fun delete(user: User)

@Query("DELETE FROM user_table")
suspend fun deleteAll()
}

In UserDao, we define several methods:

  • insert(user: User) inserts a new user into the database.
  • getUsers() retrieves all users ordered by name.
  • update(user: User) updates an existing user's information.
  • delete(user: User) deletes a specific user.
  • deleteAll() deletes all users from the database.

3. Setting up the Database

The UserDatabase class is the main access point to the persisted data. It extends RoomDatabase and includes an abstract method to get the DAO.

…/data/UserDatabase.kt

@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao

companion object {
@Volatile
private var INSTANCE: UserDatabase? = null

fun getDatabase(context: Context): UserDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java,
"user_database"
).build()
INSTANCE = instance
instance
}
}
}
}

In UserDatabase, the @Database annotation specifies the entity and version of the database. The getDatabase method ensures a single instance of the database is created (singleton pattern).

4. Creating the Repository

The repository class abstracts access to multiple data sources and provides a clean API for data access to the rest of the application.

…/data/UserRepository.kt

class UserRepository(private val userDao: UserDao) {
val allUsers: Flow<List<User>> = userDao.getUsers()

suspend fun insert(user: User) {
userDao.insert(user)
}

suspend fun update(user: User) {
userDao.update(user)
}

suspend fun delete(user: User) {
userDao.delete(user)
}

suspend fun deleteAll() {
userDao.deleteAll()
}
}

5. Dependency Injection Setup

Using Koin for dependency injection, we define a module to provide the necessary dependencies.

In UserRepository, we define methods for data operations which internally call the DAO methods. This provides a clear separation of concerns, keeping the data logic separate from the UI logic.

…/di/AppModule.kt

val appModule = module {
single { UserDatabase.getDatabase(androidContext()).userDao() }
single { UserRepository(get()) }
}

In AppModule, we define how to provide instances of UserDao, UserRepository. This ensures that our components are easily testable and maintainable.

Hint: branch name: 02-room-database

Unlisted

--

--