ตั้งค่า SQLite สำหรับ KMP

ไลบรารี androidx.sqlite มีอินเทอร์เฟซแบบนามธรรมพร้อมกับการใช้งานพื้นฐานซึ่งสามารถใช้สร้างไลบรารีของคุณเองที่เข้าถึง SQLite ได้ คุณอาจพิจารณาใช้ไลบรารี Room ซึ่งมี เลเยอร์การแยกส่วนเหนือ SQLite เพื่อให้เข้าถึงฐานข้อมูลได้อย่างมีประสิทธิภาพมากขึ้นในขณะที่ ใช้ประโยชน์จากความสามารถทั้งหมดของ SQLite

ตั้งค่าทรัพยากร Dependency

หากต้องการตั้งค่า SQLite ในโปรเจ็กต์ KMP ให้เพิ่มทรัพยากร Dependency สำหรับอาร์ติแฟกต์ในไฟล์ build.gradle.kts ของโมดูล

[versions]
sqlite = "2.7.0"

[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" }

SQLite Driver API

กลุ่มไลบรารี androidx.sqlite มี API ระดับต่ำสำหรับการสื่อสารกับ ไลบรารี SQLite ซึ่งรวมอยู่ในไลบรารีเมื่อใช้ androidx.sqlite:sqlite-bundled หรือในแพลตฟอร์มโฮสต์ เช่น Android หรือ iOS เมื่อใช้ androidx.sqlite:sqlite-framework API เหล่านี้ทำงานตามฟังก์ชันการทำงานหลักของ SQLite C API อย่างใกล้ชิด

อินเทอร์เฟซหลักมี 3 รายการ ได้แก่

  • SQLiteDriver: จุดเริ่มต้นของ SQLite ซึ่งใช้เพื่อเปิดการเชื่อมต่อฐานข้อมูล
  • SQLiteConnection: แสดงออบเจ็กต์ sqlite3
  • SQLiteStatement: แสดงออบเจ็กต์ sqlite3_stmt

ตัวอย่างต่อไปนี้แสดง API หลัก

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()
}

รูปแบบการใช้งานทั่วไปประกอบด้วยขั้นตอนต่อไปนี้ ซึ่งคล้ายกับ SQLite C API

  1. เปิดการเชื่อมต่อฐานข้อมูลโดยใช้การใช้งาน SQLiteDriver ที่สร้างอินสแตนซ์
  2. เตรียมคำสั่ง SQL โดยใช้ SQLiteConnection.prepare
  3. เรียกใช้ SQLiteStatement โดยทำดังนี้
    1. ไม่บังคับ: ผูกอาร์กิวเมนต์โดยใช้ฟังก์ชัน bind*
    2. วนซ้ำชุดผลลัพธ์โดยใช้ฟังก์ชัน step
    3. อ่านคอลัมน์จากชุดผลลัพธ์โดยใช้ฟังก์ชัน get*

การใช้งานไดรเวอร์

ตารางต่อไปนี้สรุปการใช้งานไดรเวอร์ที่มี

ชื่อชั้นเรียน

อาร์ติแฟกต์

แพลตฟอร์มที่รองรับ

AndroidSQLiteDriver androidx.sqlite:sqlite-framework

Android

NativeSQLiteDriver androidx.sqlite:sqlite-framework

iOS, Mac และ Linux

BundledSQLiteDriver androidx.sqlite:sqlite-bundled

Android, iOS, Mac, Linux และ JVM (เดสก์ท็อป)

WebWorkerSQLiteDriver androidx.sqlite:sqlite-web

JavaScript และ WebAssembly (WasmJS)

การใช้งานที่แนะนำคือ BundledSQLiteDriver ซึ่งมีอยู่ใน androidx.sqlite:sqlite-bundled โดยรวมถึงไลบรารี SQLite ที่คอมไพล์จากซอร์สโค้ด ซึ่งมีเวอร์ชันที่ทันสมัยที่สุดและความสอดคล้องกันในแพลตฟอร์ม KMP ที่รองรับทั้งหมด

SQLite Driver และ Room

API ไดรเวอร์มีประโยชน์สำหรับการโต้ตอบระดับต่ำกับฐานข้อมูล SQLite เราขอแนะนำให้ใช้ Room ซึ่งเป็นไลบรารีที่มีฟีเจอร์มากมายและให้การเข้าถึง SQLite ที่มีประสิทธิภาพมากขึ้น

A RoomDatabase อาศัย SQLiteDriver ในการดำเนินการฐานข้อมูล และคุณต้องกำหนดค่าการใช้งานโดยใช้ RoomDatabase.Builder.setDriver Room มี RoomDatabase.useReaderConnection และ RoomDatabase.useWriterConnection เพื่อให้เข้าถึงการเชื่อมต่อ ฐานข้อมูลที่มีการจัดการได้โดยตรงมากขึ้น

ย้ายข้อมูลไปยัง Kotlin Multiplatform

คุณต้องย้ายข้อมูลการใช้คอมโพเนนต์ Support SQLite API ระดับต่ำ เช่น อินเทอร์เฟซ SupportSQLiteDatabase ไปยังคอมโพเนนต์ SQLite Driver ที่เทียบเท่า

Kotlin Multiplatform

ทำธุรกรรมโดยใช้ SQLiteConnection ระดับต่ำ

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")
}

เรียกใช้การค้นหาที่ไม่มีผลลัพธ์

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

เรียกใช้การค้นหาที่มีผลลัพธ์แต่ไม่มีอาร์กิวเมนต์

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

เรียกใช้การค้นหาที่มีผลลัพธ์และอาร์กิวเมนต์

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

Android เท่านั้น

ทำธุรกรรมโดยใช้ SupportSQLiteDatabase

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

เรียกใช้การค้นหาที่ไม่มีผลลัพธ์

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

เรียกใช้การค้นหาที่มีผลลัพธ์แต่ไม่มีอาร์กิวเมนต์

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

เรียกใช้การค้นหาที่มีผลลัพธ์และอาร์กิวเมนต์

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