Room Database
1. Giới thiệu
Phần tiêu đề “1. Giới thiệu”Room là abstraction layer trên SQLite, cung cấp compile-time verification và Kotlin Coroutines support.
2. Setup
Phần tiêu đề “2. Setup”plugins { id("com.google.devtools.ksp")}
dependencies { implementation("androidx.room:room-runtime:2.6.1") implementation("androidx.room:room-ktx:2.6.1") ksp("androidx.room:room-compiler:2.6.1")}3. Components của Room
Phần tiêu đề “3. Components của Room”@Entity ──────► Table trong database@Dao ─────────► Data Access Object (queries)@Database ────► Database holder4. Entity
Phần tiêu đề “4. Entity”@Entity(tableName = "users")data class User( @PrimaryKey(autoGenerate = true) val id: Int = 0,
@ColumnInfo(name = "user_name") val name: String,
val email: String,
@ColumnInfo(name = "created_at") val createdAt: Long = System.currentTimeMillis())Entity với Foreign Key
Phần tiêu đề “Entity với Foreign Key”@Entity( tableName = "posts", foreignKeys = [ ForeignKey( entity = User::class, parentColumns = ["id"], childColumns = ["user_id"], onDelete = ForeignKey.CASCADE ) ], indices = [Index("user_id")])data class Post( @PrimaryKey(autoGenerate = true) val id: Int = 0,
@ColumnInfo(name = "user_id") val userId: Int,
val title: String, val content: String)5. DAO (Data Access Object)
Phần tiêu đề “5. DAO (Data Access Object)”@Daointerface UserDao {
@Query("SELECT * FROM users") fun getAllUsers(): Flow<List<User>>
@Query("SELECT * FROM users WHERE id = :userId") suspend fun getUserById(userId: Int): User?
@Query("SELECT * FROM users WHERE user_name LIKE :search") fun searchUsers(search: String): Flow<List<User>>
@Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(user: User): Long
@Insert suspend fun insertAll(users: List<User>)
@Update suspend fun update(user: User)
@Delete suspend fun delete(user: User)
@Query("DELETE FROM users") suspend fun deleteAll()
@Query("SELECT COUNT(*) FROM users") suspend fun getUserCount(): Int}6. Database
Phần tiêu đề “6. Database”@Database( entities = [User::class, Post::class], version = 1, exportSchema = true)abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao abstract fun postDao(): PostDao}Singleton Pattern
Phần tiêu đề “Singleton Pattern”object DatabaseProvider { @Volatile private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase { return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, "app_database" ) .fallbackToDestructiveMigration() .build() INSTANCE = instance instance } }}7. Repository Pattern
Phần tiêu đề “7. Repository Pattern”class UserRepository(private val userDao: UserDao) {
val allUsers: Flow<List<User>> = userDao.getAllUsers()
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 getUserById(id: Int) = userDao.getUserById(id)}8. ViewModel Integration
Phần tiêu đề “8. ViewModel Integration”class UserViewModel(private val repository: UserRepository) : ViewModel() {
val allUsers: StateFlow<List<User>> = repository.allUsers .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = emptyList() )
fun addUser(name: String, email: String) { viewModelScope.launch { repository.insert(User(name = name, email = email)) } }
fun deleteUser(user: User) { viewModelScope.launch { repository.delete(user) } }}9. Compose Integration
Phần tiêu đề “9. Compose Integration”@Composablefun UserListScreen(viewModel: UserViewModel = viewModel()) { val users by viewModel.allUsers.collectAsState()
LazyColumn { items(users, key = { it.id }) { user -> UserItem( user = user, onDelete = { viewModel.deleteUser(user) } ) } }}10. Type Converters
Phần tiêu đề “10. Type Converters”class Converters { @TypeConverter fun fromTimestamp(value: Long?): Date? { return value?.let { Date(it) } }
@TypeConverter fun dateToTimestamp(date: Date?): Long? { return date?.time }
@TypeConverter fun fromStringList(value: List<String>): String { return Gson().toJson(value) }
@TypeConverter fun toStringList(value: String): List<String> { return Gson().fromJson(value, object : TypeToken<List<String>>() {}.type) }}
@Database(...)@TypeConverters(Converters::class)abstract class AppDatabase : RoomDatabase()11. Migrations
Phần tiêu đề “11. Migrations”val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE users ADD COLUMN avatar TEXT") }}
Room.databaseBuilder(...) .addMigrations(MIGRATION_1_2) .build()12. Relations
Phần tiêu đề “12. Relations”data class UserWithPosts( @Embedded val user: User, @Relation( parentColumn = "id", entityColumn = "user_id" ) val posts: List<Post>)
@Daointerface UserDao { @Transaction @Query("SELECT * FROM users") fun getUsersWithPosts(): Flow<List<UserWithPosts>>}📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Component | Mô tả |
|---|---|
@Entity |
Table definition |
@Dao |
Data access methods |
@Database |
Database holder |
@Query |
Custom SQL queries |
@Insert/@Update/@Delete |
CRUD operations |
@TypeConverter |
Custom type conversion |
@Relation |
Entity relationships |