Thư viện lưu trữ Room cung cấp một tầng trừu tượng qua SQLite để giúp truy cập cơ sở dữ liệu hiệu quả hơn, đồng thời khai thác toàn bộ sức mạnh của SQLite. Trang này tập trung vào việc sử dụng Room trong các dự án Kotlin Multiplatform (KMP). Để biết thêm thông tin về cách sử dụng Room, hãy xem bài viết Lưu dữ liệu trong cơ sở dữ liệu cục bộ bằng Room hoặc mẫu chính thức của chúng tôi.
Thiết lập phần phụ thuộc
Phiên bản hiện tại của Room hỗ trợ KMP là 2.7.0-alpha01 trở lên.
Để thiết lập Room trong dự án KMP, hãy thêm các phần phụ thuộc cho cấu phần phần mềm trong tệp build.gradle.kts
cho mô-đun của bạn:
androidx.room:room-gradle-plugin
– Trình bổ trợ Gradle để định cấu hình giản đồ Roomandroidx.room:room-compiler
– Bộ xử lý KSP tạo mãandroidx.room:room-runtime
– Phần thời gian chạy của thư việnandroidx.sqlite:sqlite-bundled
– (Không bắt buộc) Thư viện SQLite đi kèm
Ngoài ra, bạn cần định cấu hình trình điều khiển SQLite của Room. Những trình điều khiển này khác nhau tuỳ theo nền tảng mục tiêu. Xem phần Triển khai trình điều khiển để biết nội dung mô tả các cách triển khai trình điều khiển hiện có.
Để biết thêm thông tin về cách thiết lập, hãy xem phần dưới đây:
- Đặt vị trí giản đồ bằng trình bổ trợ Room cho Gradle.
- KSP với Kotlin Multiplatform.
- Thêm phần phụ thuộc thời gian chạy.
Xác định các lớp cơ sở dữ liệu
Bạn cần tạo một lớp cơ sở dữ liệu được chú giải bằng @Database
cùng với DAO và thực thể bên trong nhóm tài nguyên chung của mô-đun KMP dùng chung. Việc đặt các lớp này trong các nguồn chung sẽ cho phép chia sẻ các lớp này trên tất cả nền tảng mục tiêu.
// shared/src/commonMain/kotlin/Database.kt
@Database(entities = [TodoEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
}
@Dao
interface TodoDao {
@Insert
suspend fun insert(item: TodoEntity)
@Query("SELECT count(*) FROM TodoEntity")
suspend fun count(): Int
@Query("SELECT * FROM TodoEntity")
fun getAllAsFlow(): Flow<List<TodoEntity>>
}
@Entity
data class TodoEntity(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val content: String
)
Xin lưu ý rằng bạn có thể sử dụng nội dung khai báo thực tế / dự kiến để tạo các hoạt động triển khai Room dành riêng cho nền tảng. Ví dụ: bạn có thể thêm một DAO dành riêng cho nền tảng được xác định trong mã phổ biến bằng cách sử dụng expect
, sau đó chỉ định các định nghĩa actual
với các truy vấn bổ sung trong các nhóm tài nguyên dành riêng cho nền tảng.
Tạo trình tạo cơ sở dữ liệu
Bạn cần xác định trình tạo cơ sở dữ liệu để tạo thực thể cho Room trên từng nền tảng. Đây là phần duy nhất của API cần thiết trong các nhóm tài nguyên dành riêng cho nền tảng do sự khác biệt về API hệ thống tệp. Ví dụ: trong Android, thông tin vị trí của cơ sở dữ liệu thường được lấy thông qua API Context.getDatabasePath()
, trong khi đối với iOS, thông tin vị trí của cơ sở dữ liệu thường được lấy bằng NSHomeDirectory
.
Android
Để tạo thực thể cơ sở dữ liệu, hãy chỉ định một Context cùng với đường dẫn cơ sở dữ liệu. Bạn không cần chỉ định nhà máy cơ sở dữ liệu.
// shared/src/androidMain/kotlin/Database.kt
fun getDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
val appContext = ctx.applicationContext
val dbFile = appContext.getDatabasePath("my_room.db")
return Room.databaseBuilder<AppDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}
iOS
Để tạo phiên bản cơ sở dữ liệu, hãy cung cấp một nhà máy cơ sở dữ liệu cùng với đường dẫn cơ sở dữ liệu. Nhà máy cơ sở dữ liệu là một hàm lambda gọi một hàm mở rộng đã tạo có tên là instantiateImpl
với phương thức nhận thuộc loại KClass<T>
, trong đó T
là loại của lớp được chú thích @Database
.
// shared/src/iosMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFilePath = NSHomeDirectory() + "/my_room.db"
return Room.databaseBuilder<AppDatabase>(
name = dbFilePath,
factory = { AppDatabase::class.instantiateImpl() }
)
}
JVM (Máy tính)
Để tạo phiên bản thể hiện của cơ sở dữ liệu, chỉ chỉ định đường dẫn cơ sở dữ liệu. Bạn không cần cung cấp nhà máy cơ sở dữ liệu.
// shared/src/commonMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
return Room.databaseBuilder<AppDatabase>(
name = dbFile.absolutePath,
)
}
Tạo thực thể cơ sở dữ liệu
Sau khi lấy RoomDatabase.Builder
từ một trong các hàm khởi tạo dành riêng cho nền tảng, bạn có thể định cấu hình phần còn lại của cơ sở dữ liệu Room bằng mã chung cùng với việc tạo bản sao cơ sở dữ liệu thực tế.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
Chọn SQLiteDriver
Các đoạn mã trước đó sử dụng BundledSQLiteDriver
. Đây là trình điều khiển
được đề xuất bao gồm SQLite được biên dịch từ nguồn, cung cấp
phiên bản SQLite nhất quán và mới nhất trên tất cả nền tảng. Nếu bạn muốn sử dụng SQLite do hệ điều hành cung cấp, hãy sử dụng API setDriver
trong các nhóm tài nguyên dành riêng cho nền tảng chỉ định trình điều khiển dành riêng cho nền tảng. Đối với Android, bạn có thể sử dụng AndroidSQLiteDriver
, còn đối với iOS, bạn có thể sử dụng NativeSQLiteDriver
. Để sử dụng NativeSQLiteDriver
, bạn cần cung cấp tuỳ chọn trình liên kết để ứng dụng iOS liên kết động với SQLite của hệ thống.
// shared/build.gradle.kts
kotlin {
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "TodoApp"
isStatic = true
// Required when using NativeSQLiteDriver
linkerOpts.add("-lsqlite3")
}
}
}
Điểm khác biệt
Ban đầu, Room được phát triển dưới dạng một thư viện Android và sau đó được chuyển sang KMP tập trung vào khả năng tương thích với API. Phiên bản KMP của Room khác nhau đôi chút giữa các nền tảng và phiên bản dành riêng cho Android. Những điểm khác biệt này được liệt kê và mô tả như sau.
Chặn các hàm DAO
Khi sử dụng Room cho KMP, mọi hàm DAO được biên dịch cho nền tảng không phải Android cần phải là các hàm suspend
, ngoại trừ các loại dữ liệu trả về phản ứng, chẳng hạn như Flow
.
// shared/src/commonMain/kotlin/MultiplatformDao.kt
@Dao
interface MultiplatformDao {
// ERROR: Blocking function not valid for non-Android targets
@Query("SELECT * FROM Entity")
fun blockingQuery(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
suspend fun query(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
fun queryFlow(): Flow<List<Entity>>
// ERROR: Blocking function not valid for non-Android targets
@Transaction
fun blockingTransaction() { // … }
// OK
@Transaction
suspend fun transaction() { // … }
}
Room hưởng lợi từ thư viện kotlinx.coroutines
không đồng bộ với nhiều tính năng phong phú mà Kotlin cung cấp cho nhiều nền tảng. Để tối ưu hoá chức năng, các hàm suspend
được thực thi cho các DAO được biên dịch trong dự án KMP, ngoại trừ các DAO dành riêng cho Android để duy trì khả năng tương thích ngược với cơ sở mã hiện có.
Sự khác biệt về tính năng so với KMP
Phần này mô tả sự khác biệt về các tính năng giữa các phiên bản Room của nền tảng Android và KMP.
Các hàm DAO @RawQuery
Các hàm được chú thích bằng @RawQuery
được biên dịch cho nền tảng không phải Android sẽ gây ra lỗi. Chúng tôi dự định sẽ hỗ trợ thêm cho @RawQuery
trong phiên bản Room trong tương lai.
Lệnh gọi lại truy vấn
Các API sau đây để định cấu hình lệnh gọi lại truy vấn không được cung cấp phổ biến và do đó không được cung cấp trên các nền tảng khác ngoài Android.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
Chúng tôi dự định sẽ hỗ trợ thêm lệnh gọi lại truy vấn trong phiên bản Room trong tương lai.
API để định cấu hình RoomDatabase
bằng lệnh gọi lại truy vấn RoomDatabase.Builder.setQueryCallback
cùng với giao diện gọi lại RoomDatabase.QueryCallback
không được cung cấp phổ biến, do đó không có trong các nền tảng khác ngoài Android.
Tự động đóng cơ sở dữ liệu
API để cho phép tự động đóng sau khi hết thời gian chờ, RoomDatabase.Builder.setAutoCloseTimeout
, chỉ có trên Android và không có trong các nền tảng khác.
Cơ sở dữ liệu đóng gói trước
Các API sau đây để tạo RoomDatabase
bằng cơ sở dữ liệu hiện có (tức là cơ sở dữ liệu đóng gói trước) không có sẵn trong các nền tảng phổ biến nên không có trong các nền tảng khác ngoài Android. Các API này là:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
Chúng tôi dự định thêm tính năng hỗ trợ cho cơ sở dữ liệu đóng gói sẵn trong phiên bản Room trong tương lai.
Vô hiệu hoá nhiều phiên bản
API để cho phép vô hiệu hoá nhiều phiên bản, RoomDatabase.Builder.enableMultiInstanceInvalidation
chỉ có trên Android và không được hỗ trợ trong các nền tảng phổ biến hoặc các nền tảng khác.