4. Integrate Room database with entities, DAO, and repository
--
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