
Room trong Android và Các Vấn Đề Thường Gặp
Cách khai báo và cài đặt
Cài đặt dependencies
def room_version = "2.7.1"
implementation "androidx.room:room-runtime:$room_version"
ksp("androidx.room:room-compiler:$room_version")
implementation "androidx.room:room-ktx:$room_version"
Khai báo các thành phần Room
@Entity
data class User(
@PrimaryKey val uid: Int,
val name: String
)
@Dao
interface UserDao {
@Query("SELECT * FROM user")
suspend fun getAll(): List<User>
@Insert
suspend fun insert(user: User)
}
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Khởi tạo database
val db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "my-database"
).build()
Quan hệ 1-1, 1-n, n-n trong Room
1 - 1: User - Profile
@Entity
data class Profile(
@PrimaryKey val userId: Int,
val bio: String
)
data class UserWithProfile(
@Embedded val user: User,
@Relation(
parentColumn = "uid",
entityColumn = "userId"
)
val profile: Profile
)
Get
@Transaction
@Query("SELECT * FROM user WHERE uid = :userId")
suspend fun getUserWithProfile(userId: Int): UserWithProfile
Insert/Update
@Insert suspend fun insertUser(user: User)
@Insert suspend fun insertProfile(profile: Profile)
@Update suspend fun updateProfile(profile: Profile)
1 - N: User - Posts
@Entity
data class Post(
@PrimaryKey val id: Int,
val userId: Int,
val content: String
)
data class UserWithPosts(
@Embedded val user: User,
@Relation(
parentColumn = "uid",
entityColumn = "userId"
)
val posts: List<Post>
)
Get
@Transaction
@Query("SELECT * FROM user WHERE uid = :userId")
suspend fun getUserWithPosts(userId: Int): UserWithPosts
Insert/Update
@Insert suspend fun insertPosts(posts: List<Post>)
@Update suspend fun updatePost(post: Post)
N - N: User - Project qua bảng trung gian
@Entity
data class Project(
@PrimaryKey val projectId: Int,
val name: String
)
@Entity(primaryKeys = ["userId", "projectId"])
data class UserProjectCrossRef(
val userId: Int,
val projectId: Int
)
data class UserWithProjects(
@Embedded val user: User,
@Relation(
parentColumn = "uid",
entityColumn = "projectId",
associateBy = Junction(UserProjectCrossRef::class)
)
val projects: List<Project>
)
Get
@Transaction
@Query("SELECT * FROM user WHERE uid = :userId")
suspend fun getUserWithProjects(userId: Int): UserWithProjects
Insert
@Insert suspend fun insertUser(user: User)
@Insert suspend fun insertProject(project: Project)
@Insert suspend fun insertUserProjectCrossRef(ref: UserProjectCrossRef)
Migration - Nâng cấp schema
Tạo migration thủ công
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE user ADD COLUMN age INTEGER NOT NULL DEFAULT 0")
}
}
Thêm vào database builder
Room.databaseBuilder(
context,
AppDatabase::class.java, "my-database"
).addMigrations(MIGRATION_1_2).build()
Fallback (xoá sạch dữ liệu)
Room.databaseBuilder(
context,
AppDatabase::class.java, "my-database"
).fallbackToDestructiveMigration().build()
Lắng nghe thay đổi bằng Flow
Khai báo DAO trả về Flow
@Query("SELECT * FROM user")
fun getAllUsers(): Flow<List<User>>
Trong ViewModel
val usersFlow: Flow<List<User>> = userDao.getAllUsers()
Hoặc lưu vào StateFlow:
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users
init {
viewModelScope.launch {
userDao.getAllUsers().collect {
_users.value = it
}
}
}
Trong Compose
@Composable
fun UserList(viewModel: UserViewModel) {
val users by viewModel.users.collectAsStateWithLifecycle()
LazyColumn {
items(users, key = { it.uid }) { user ->
Text(user.name)
}
}
}
Tổng kết
- Room giúp thao tác SQLite đơn giản và an toàn hơn
- Dùng
@Relation,@Embeddedđể xử lý quan hệ giữa các bảng - Tự quản lý
insert,update,deletetrong quan hệ Flowgiúp lắng nghe thay đổi dữ liệu theo thời gian thực- Luôn dùng
@Transactionvới truy vấn có@Relation - Sử dụng migration cẩn thận để đảm bảo dữ liệu không bị mất

