Biblioteka androidx.sqlite
zawiera abstrakcyjne interfejsy wraz z podstawowymi implementacjami, których można używać do tworzenia własnych bibliotek, które mają dostęp do SQLite. Warto rozważyć użycie biblioteki Room, która zapewnia warstwę abstrakcji nad SQLite, aby umożliwić bardziej niezawodny dostęp do bazy danych przy jednoczesnym wykorzystaniu pełnej mocy SQLite.
Konfigurowanie zależności
Aby skonfigurować SQLite w projekcie KMP, dodaj zależności artefaktów w pliku build.gradle.kts
modułu:
[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" }
Interfejsy API sterownika SQLite
Biblioteki androidx.sqlite
oferują interfejsy API niskiego poziomu do komunikacji z biblioteką SQLite, która jest dołączana do biblioteki podczas korzystania z androidx.sqlite:sqlite-bundled
lub na platformie hosta, np. Androida lub iOS, podczas korzystania z androidx.sqlite:sqlite-framework
. Interfejsy API są ściśle zgodne z podstawowymi funkcjami interfejsu SQLite C API.
Dostępne są 3 główne interfejsy:
SQLiteDriver
– to punkt wejścia do korzystania z bazy danych SQLite. Odpowiada za otwieranie połączeń z bazą danych.SQLiteConnection
– reprezentacja obiektusqlite3
.SQLiteStatement
– reprezentacja obiektusqlite3_stmt
.
Poniższy przykład przedstawia podstawowe interfejsy 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()
}
Podobnie jak w przypadku interfejsów API SQLite C, typowe użycie polega na:
- Otwórz połączenie z bazą danych za pomocą utworzonego wystąpienia
SQLiteDriver
implementacji. - Przygotuj instrukcję SQL za pomocą
SQLiteConnection.prepare()
- Wykonaj
SQLiteStatement
w ten sposób:- Opcjonalnie możesz powiązać argumenty za pomocą funkcji
bind*()
. - Iteruj po zbiorze wyników za pomocą funkcji
step()
. - Odczytuj kolumny z zestawu wyników za pomocą funkcji
get*()
.
- Opcjonalnie możesz powiązać argumenty za pomocą funkcji
Implementacje sterowników
W tabeli poniżej znajdziesz podsumowanie dostępnych implementacji sterowników:
Nazwa zajęć |
Artefakt |
Obsługiwane platformy |
AndroidSQLiteDriver |
androidx.sqlite:sqlite-framework |
Android |
NativeSQLiteDriver |
androidx.sqlite:sqlite-framework |
iOS, Mac i Linux |
BundledSQLiteDriver |
androidx.sqlite:sqlite-bundled |
Android, iOS, Mac, Linux i JVM (Desktop) |
Zalecana implementacja to BundledSQLiteDriver
dostępna w androidx.sqlite:sqlite-bundled
. Zawiera bibliotekę SQLite skompilowaną z kodu źródłowego, która zapewnia najbardziej aktualną wersję i spójność na wszystkich obsługiwanych platformach KMP.
Sterownik SQLite i Room
Interfejsy API sterownika są przydatne w przypadku interakcji niskiego poziomu z bazą danych SQLite. Jeśli potrzebujesz rozbudowanej biblioteki, która zapewnia bardziej niezawodny dostęp do SQLite, zalecamy użycie Room.
RoomDatabase
korzysta z SQLiteDriver
do wykonywania operacji na bazie danych, a implementację należy skonfigurować za pomocą RoomDatabase.Builder.setDriver()
. Room udostępnia RoomDatabase.useReaderConnection
i RoomDatabase.useWriterConnection
, aby zapewnić bardziej bezpośredni dostęp do zarządzanych połączeń z bazą danych.
Migracja do Kotlin Multiplatform
Wszystkie wywołania SQLite niskiego poziomu muszą zostać przeniesione do odpowiedników w sterowniku SQLite.
Kotlin Multiplatform
Przeprowadzanie transakcji za pomocą interfejsu SQLiteConnection
niskiego poziomu
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")
}
Wykonaj zapytanie bez wyniku
val connection: SQLiteConnection = ...
connection.execSQL("ALTER TABLE ...")
Wykonaj zapytanie z wynikiem, ale bez argumentów
val connection: SQLiteConnection = ...
connection.prepare("SELECT * FROM Pet").use { statement ->
while (statement.step()) {
// read columns
statement.getInt(0)
statement.getText(1)
}
}
Wykonaj zapytanie z wynikiem i argumentami
connection.prepare("SELECT * FROM Pet WHERE id = ?").use { statement ->
statement.bindInt(1, id)
if (statement.step()) {
// row found, read columns
} else {
// row not found
}
}
Tylko Android
Przeprowadź transakcję za pomocą SupportSQLiteDatabase
val database: SupportSQLiteDatabase = ...
database.beginTransaction()
try {
// perform database operations in transaction
database.setTransactionSuccessful()
} finally {
database.endTransaction()
}
Wykonaj zapytanie bez wyniku
val database: SupportSQLiteDatabase = ...
database.execSQL("ALTER TABLE ...")
Wykonaj zapytanie z wynikiem, ale bez argumentów
val database: SupportSQLiteDatabase = ...
database.query("SELECT * FROM Pet").use { cursor ->
while (cusor.moveToNext()) {
// read columns
cursor.getInt(0)
cursor.getString(1)
}
}
Wykonaj zapytanie z wynikiem i argumentami
database.query("SELECT * FROM Pet WHERE id = ?", id).use { cursor ->
if (cursor.moveToNext()) {
// row found, read columns
} else {
// row not found
}
}