SQLDelight für Kotlin: Komplettguide mit Beispielen

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

SQLDelight für Kotlin: Komplettguide mit Beispielen

1. Was ist SQLDelight und warum braucht man es?

SQLDelight ist eine Bibliothek für Kotlin, die aus SQL-Abfragen type-sicheren Kotlin-Code generiert. Entwickelt von Cash App (Square), unterstützt sie Kotlin Multiplatform (KMP) und ermöglicht die Nutzung desselben Codes für Datenbankoperationen auf Android, iOS, JVM, JS und Native-Plattformen.

Die Kernidee von SQLDelight: Du schreibst SQL-Abfragen in .sq-Dateien, und die Bibliothek generiert automatisch Kotlin-Klassen, Interfaces und Funktionen, die exakt deinen Abfragen entsprechen. Das eliminiert Boilerplate-Code für das Mapping von Abfrageergebnissen auf Objekte und verhindert SQL-Injection bereits zur Kompilierzeit.

Warum SQLDelight verwenden? Erstens bietet es vollständige Typprüfung zur Kompilierzeit: Änderst du die Tabellenstruktur, zeigt der Compiler sofort alle Stellen an, an denen Abfragen aktualisiert werden müssen. Zweitens unterstützt es Migrationen, Transaktionen, asynchrone Abfragen und Multiplattform-Entwicklung. Drittens lässt es sich nahtlos in Frameworks wie Ktor, Koin, Dagger/Hilt und andere integrieren.

2. Installation

SQLDelight wird über das Gradle-Build-System installiert. Für Kotlin Multiplatform füge das folgende Plugin in deine build.gradle.kts (Root) ein:

// Root build.gradle.kts
plugins {
    id("app.cash.sqldelight") version "2.0.2" apply false
}

// Im Modul (z. B. :shared)
plugins {
    id("app.cash.sqldelight")
}

sqldelight {
    databases {
        create("MyDatabase") {
            packageName.set("com.example.db")
        }
    }
}

dependencies {
    implementation("app.cash.sqldelight:coroutines-extensions:2.0.2")
    implementation("app.cash.sqldelight:runtime:2.0.2")
    // Für Android
    implementation("app.cash.sqldelight:android-driver:2.0.2")
    // Für iOS
    implementation("app.cash.sqldelight:native-driver:2.0.2")
    // Für JVM
    implementation("app.cash.sqldelight:sqlite-driver:2.0.2")
}

Nach der Gradle-Synchronisierung erstellst du die Datei src/commonMain/sqldelight/com/example/db/MyDatabase.sq (der Pfad muss mit packageName übereinstimmen).

3. Schnellstart – Minimales Arbeitsbeispiel

Erstelle die Datei MyDatabase.sq:

-- MyDatabase.sq
CREATE TABLE Person (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    age INTEGER NOT NULL
);

selectAll:
SELECT * FROM Person;

insertPerson:
INSERT INTO Person(name, age) VALUES (?, ?);

Jetzt im Kotlin-Code:

import com.example.db.MyDatabase
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver

fun main() {
    val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:test.db")
    MyDatabase.Schema.create(driver)
    
    val database = MyDatabase(driver)
    val queries = database.myDatabaseQueries
    
    // Einfügen
    queries.insertPerson("Alice", 30)
    queries.insertPerson("Bob", 25)
    
    // Abfragen
    val people = queries.selectAll().executeAsList()
    people.forEach { person ->
        println("${person.name} ist ${person.age} Jahre alt")
    }
}

Dieser Code erstellt die Tabelle, fügt zwei Datensätze ein und gibt sie aus.

4. Wichtige Methoden/Funktionen/Klassen

4.1 executeAsList()

Signatur: fun <T> executeAsList(): List<T>

Funktion: Führt eine SELECT-Abfrage aus und gibt alle Ergebniszeilen als Liste von Objekten des generierten Typs zurück.

val allPeople: List<Person> = queries.selectAll().executeAsList()

4.2 executeAsOne()

Signatur: fun <T> executeAsOne(): T

Funktion: Führt eine SELECT-Abfrage aus, die genau eine Zeile zurückgibt. Wirft eine Exception, wenn keine oder mehr als eine Zeile vorhanden sind.

val person: Person = queries.selectById(1).executeAsOne()

4.3 executeAsOneOrNull()

Signatur: fun <T> executeAsOneOrNull(): T?

Funktion: Wie executeAsOne(), gibt aber null zurück, wenn keine Zeile gefunden wird.

val person: Person? = queries.selectById(99).executeAsOneOrNull()

4.4 execute()

Signatur: fun execute(): QueryResult<Long>

Funktion: Führt eine INSERT-, UPDATE- oder DELETE-Abfrage aus. Gibt die Anzahl der betroffenen Zeilen zurück.

val rowsAffected = queries.deletePerson(1).execute()

4.5 asFlow() / .asFlow().mapToList()

Signatur: fun <T> asFlow(): Flow<Query<T>>

Funktion: Erstellt einen reaktiven Flow, der bei jeder Änderung der zugrunde liegenden Tabelle neue Ergebnisse liefert. Ideal für Echtzeit-Updates in der UI.

queries.selectAll()
    .asFlow()
    .mapToList()
    .collect { people ->
        // Wird bei jeder Änderung der Person-Tabelle aufgerufen
        println("Personen aktualisiert: $people")
    }

4.6 Transaktionen mit transaction()

Signatur: fun <T> transaction(noEnclosing: Boolean = false, body: Transaction.() -> T): T

Funktion: Führt mehrere Datenbankoperationen in einer atomaren Transaktion aus. Bei einem Fehler wird automatisch ein Rollback durchgeführt.

database.transaction {
    queries.insertPerson("Charlie", 35)
    queries.insertPerson("Diana", 28)
    // Beide Einfügungen werden gemeinsam committed oder zurückgerollt
}

5. Fazit

SQLDelight ist eine leistungsstarke und elegante Lösung für die Datenbankarbeit in Kotlin Multiplatform-Projekten. Durch die Generierung von type-safe Code aus SQL-Abfragen reduziert es Fehler, verbessert die Wartbarkeit und beschleunigt die Entwicklung. Die Integration mit Coroutines und Flow macht es zudem ideal für moderne, reaktive Anwendungen. Starte noch heute mit SQLDelight und profitiere von einer sauberen, plattformunabhängigen Datenbankschicht!

Empfehlungen