SQLite (đa nền tảng Kotlin)

Thư viện androidx.sqlite chứa các lớp giao tiếp trừu tượng cùng với các hàm triển khai cơ bản giúp bạn tự xây dựng những thư viện liên kết với SQLite. Bạn nên cân nhắc sử dụng thư viện Room. Thư viện này cung cấp một lớp trừu tượng thông qua SQLite để cho phép truy cập vào cơ sở dữ liệu hiệu quả hơn, đồng thời khai thác tối đa sức mạnh của SQLite.

Thiết lập phần phụ thuộc

Để thiết lập SQLite 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 của mô-đun:

[versions]
sqlite = "2.5.2"

[libraries]
# The SQLite Driver interfaces
androidx-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "sqlite" }

# The bundled SQLite driver implementation
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" }

[plugins]
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

API trình điều khiển SQLite

Các nhóm thư viện androidx.sqlite cung cấp API cấp thấp để giao tiếp với thư viện SQLite có trong thư viện khi dùng androidx.sqlite:sqlite-bundled hoặc trong nền tảng lưu trữ, chẳng hạn như Android hoặc iOS khi dùng androidx.sqlite:sqlite-framework. Các API này tuân theo sát chức năng cốt lõi của SQLite C API.

Có 3 giao diện chính:

  • SQLiteDriver – Đây là điểm truy cập để sử dụng SQLite và chịu trách nhiệm mở các kết nối cơ sở dữ liệu.
  • SQLiteConnection – Là giá trị biểu thị của đối tượng sqlite3.
  • SQLiteStatement – Là giá trị biểu thị của đối tượng sqlite3_stmt.

Ví dụ sau đây minh hoạ các API cốt lõi:

fun main() {
  val databaseConnection = BundledSQLiteDriver().open("todos.db")
  databaseConnection.execSQL(
    "CREATE TABLE IF NOT EXISTS Todo (id INTEGER PRIMARY KEY, content TEXT)"
  )
  databaseConnection.prepare(
    "INSERT OR IGNORE INTO Todo (id, content) VALUES (? ,?)"
  ).use { stmt ->
    stmt.bindInt(index = 1, value = 1)
    stmt.bindText(index = 2, value = "Try Room in the KMP project.")
    stmt.step()
  }
  databaseConnection.prepare("SELECT content FROM Todo").use { stmt ->
    while (stmt.step()) {
      println("Action item: ${stmt.getText(0)}")
    }
  }
  databaseConnection.close()
}

Tương tự như các API C của SQLite, cách sử dụng phổ biến là:

  • Mở một kết nối cơ sở dữ liệu bằng cách sử dụng quá trình triển khai SQLiteDriver được khởi tạo.
  • Chuẩn bị câu lệnh SQL bằng cách sử dụng SQLiteConnection.prepare()
  • Thực thi một SQLiteStatement theo cách sau:
    1. Bạn có thể liên kết các đối số bằng hàm bind*().
    2. Lặp lại trên tập kết quả bằng hàm step().
    3. Đọc các cột từ tập kết quả bằng cách sử dụng các hàm get*().

Triển khai trình điều khiển

Bảng sau đây tóm tắt các cách triển khai trình điều khiển hiện có:

Tên lớp

Cấu phần phần mềm

Nền tảng được hỗ trợ

AndroidSQLiteDriver androidx.sqlite:sqlite-framework

Android

NativeSQLiteDriver androidx.sqlite:sqlite-framework

iOS, Mac và Linux

BundledSQLiteDriver androidx.sqlite:sqlite-bundled

Android, iOS, Mac, Linux và JVM (Máy tính)

Bạn nên sử dụng phương thức triển khai BundledSQLiteDriver có trong androidx.sqlite:sqlite-bundled. Thư viện này bao gồm thư viện SQLite được biên dịch từ nguồn, cung cấp phiên bản mới nhất và tính nhất quán trên tất cả các nền tảng KMP được hỗ trợ.

Trình điều khiển SQLite và Room

API trình điều khiển rất hữu ích cho các hoạt động tương tác cấp thấp với cơ sở dữ liệu SQLite. Đối với một thư viện giàu tính năng cung cấp khả năng truy cập mạnh mẽ hơn vào SQLite, bạn nên dùng Room.

RoomDatabase dựa vào SQLiteDriver để thực hiện các thao tác trên cơ sở dữ liệu và bạn phải định cấu hình một quy trình triển khai bằng cách sử dụng RoomDatabase.Builder.setDriver(). Room cung cấp RoomDatabase.useReaderConnectionRoomDatabase.useWriterConnection để truy cập trực tiếp hơn vào các kết nối cơ sở dữ liệu được quản lý.

Di chuyển sang Kotlin Multiplatform

Mọi hoạt động sử dụng các lệnh gọi SQLite cấp thấp đều cần được di chuyển sang các lệnh gọi tương ứng trong Trình điều khiển SQLite.

Kotlin Multiplatform

Thực hiện giao dịch bằng SQLiteConnection cấp thấp

val connection: SQLiteConnection = ...
connection.execSQL("BEGIN IMMEDIATE TRANSACTION")
try {
  // perform database operations in transaction
  connection.execSQL("END TRANSACTION")
} catch(t: Throwable) {
  connection.execSQL("ROLLBACK TRANSACTION")
}

Thực thi một truy vấn không có kết quả

val connection: SQLiteConnection = ...
connection.execSQL("ALTER TABLE ...")

Thực thi một truy vấn có kết quả nhưng không có đối số

val connection: SQLiteConnection = ...
connection.prepare("SELECT * FROM Pet").use { statement ->
  while (statement.step()) {
    // read columns
    statement.getInt(0)
    statement.getText(1)
  }
}

Thực thi một truy vấn có kết quả và đối số

connection.prepare("SELECT * FROM Pet WHERE id = ?").use { statement ->
  statement.bindInt(1, id)
  if (statement.step()) {
    // row found, read columns
  } else {
    // row not found
  }
}

Chỉ dành cho Android

Thực hiện giao dịch bằng SupportSQLiteDatabase

val database: SupportSQLiteDatabase = ...
database.beginTransaction()
try {
  // perform database operations in transaction
  database.setTransactionSuccessful()
} finally {
  database.endTransaction()
}

Thực thi một truy vấn không có kết quả

val database: SupportSQLiteDatabase = ...
database.execSQL("ALTER TABLE ...")

Thực thi một truy vấn có kết quả nhưng không có đối số

val database: SupportSQLiteDatabase = ...
database.query("SELECT * FROM Pet").use { cursor ->
  while (cusor.moveToNext()) {
    // read columns
    cursor.getInt(0)
    cursor.getString(1)
  }
}

Thực thi một truy vấn có kết quả và đối số

database.query("SELECT * FROM Pet WHERE id = ?", id).use { cursor ->
  if (cursor.moveToNext()) {
    // row found, read columns
  } else {
    // row not found
  }
}