---
title: "SQLDelightとは：SQLから型安全なKotlinコードを生成する仕組みとRoomとの選び分け【2.4系】"
url: "https://www.issoh.co.jp/tech/details/19198/"
published: 2026-11-02
updated: 2026-11-02
categories: ["プラットフォーム"]
publisher: "株式会社一創"
---

# SQLDelightとは：SQLから型安全なKotlinコードを生成する仕組みとRoomとの選び分け【2.4系】

SQLDelight（エスキューエルディライト）は、`.sq`ファイルに書いたSQLを読み、型付きのKotlinクラスとクエリ関数を生成するオープンソースのライブラリです。スキーマ・クエリ・マイグレーションの誤りはビルド時に検出されます。この記事では2026年11月時点の最新版2.4.1を前提に、AndroidでのGradle設定と`.sq`ファイルの書き方、Flowでの変更監視、マイグレーションの検証、Kotlin Multiplatform（KMP）でのドライバ構成、そしてRoom 2.8・Room 3.0との選び分けをコード付きで説明します。

## まとめ：SQLDelightを選ぶ条件とRoom 2.8・Room 3.0との使い分けの結論

SQLDelightは「SQLが正、Kotlinはそこから生成される」という設計のライブラリです。テーブル定義もクエリもSQLのまま書き、アノテーションを付けたKotlinクラスからSQLを組み立てるRoomとは出発点が逆になります。

選び分けの軸は、2026年9月にRoom 3.0の安定版が出たことで変わりました。Room 3.0はiOS・JVMに加えてJavaScriptとWasmJsもKMPのターゲットに入れたため、「KMPならSQLDelight一択」という以前の判断はもう成り立ちません。現時点でSQLDelightを選ぶのは、SQLを手で書いて管理したいチーム、PostgreSQLやMySQLを使うサーバー側のKotlinとも同じ道具を揃えたい場合、マイグレーションをSQLファイルで管理しCIで検証したい場合です。

Androidだけのアプリで、Jetpackの他ライブラリと公式サンプルに沿って進めたいなら、Roomを選んで問題ありません。

## SQLDelightの仕組み：.sqからKotlinの型とクエリ関数を生成

### Cash App発のSQLDelight：最新版2.4.1の座標と対応方言

.sqファイルに書いたSQLが、Kotlinの型とクエリ関数の生成元です。SQLDelightはCash Appの開発チームから生まれたOSSで、現在は[GitHubのsqldelight組織のリポジトリ](https://github.com/sqldelight/sqldelight)でApache-2.0ライセンスのもと公開されています。2026年11月2日時点のスター数は6,898、最新版は2026年10月6日公開の[2.4.1](https://github.com/sqldelight/sqldelight/releases/tag/2.4.1)です。2.0以降はMavenの座標が`app.cash.sqldelight`に変わっているため、1.x系の解説記事にある`com.squareup.sqldelight`の設定はそのまま使えません。

主な対象はSQLiteですが、[Gradle設定の公式リファレンス](https://sqldelight.github.io/sqldelight/latest/android%5Fsqlite/gradle/)にはMySQL・PostgreSQL・HSQLの方言モジュールも並んでいます。SQLiteの方言は既定で3.18で、Androidでは`minSdk`に合わせて自動で選ばれます。2.4.0からはGradle 8.2.1以上が必要になりました。

### SQLiteからLong・Double・String・ByteArrayへの型変換

生成されるKotlinの型は列の型で決まります。INTEGERは`Long`、REALは`Double`、TEXTは`String`、BLOBは`ByteArray`です。`NOT NULL`を付けない列はnull許容型になります。

Roomとの違いはここに出ます。Roomでは`@Entity`を付けたKotlinクラスが正で、テーブルはそこから作られます。SQLDelightではCREATE TABLE文が正で、Kotlinのデータクラスは生成物です。`SELECT`で列を絞ったクエリには、その列だけを持つ専用の型が自動で作られます。JOINの結果を受けるクラスを手で書く必要はありません。

## AndroidのSQLDelight 2.4系：Gradle設定と.sqの書き方

### Android向けGradleプラグインとandroid-driverの追加手順

[Android向けの公式手順](https://sqldelight.github.io/sqldelight/latest/android%5Fsqlite/)では、Gradleプラグインを適用し、`sqldelight`ブロックにデータベースを登録します。`register`に渡した名前が、生成されるデータベースクラスの名前になります。

```
// app/build.gradle.kts
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("app.cash.sqldelight") version "2.4.1"
}

dependencies {
    implementation("app.cash.sqldelight:android-driver:2.4.1")
    implementation("app.cash.sqldelight:coroutines-extensions:2.4.1")
}

sqldelight {
    databases {
        register("Database") {
            packageName.set("com.example")
        }
    }
}
```

Androidドライバは2.3.2から`minSdk`が23以上です。古い端末を切れないアプリでは、ここが導入の前提条件になります。IDEの補完を使うには、IntelliJまたはAndroid StudioにSQLDelightプラグインを入れます。2.4.0の時点でプラグインの最小対応はIntelliJ 2023.3、Android Studio Jellyfishです。

### Player.sqにテーブルとラベル付きクエリを書き、生成された関数を呼ぶ

SQLファイルの置き場所は`src/main/sqldelight`配下です。パッケージに合わせたディレクトリに`.sq`ファイルを作り、テーブル定義と、名前（ラベル）を付けたクエリを書きます。

```
-- src/main/sqldelight/com/example/Player.sq
CREATE TABLE hockeyPlayer (
  player_number INTEGER PRIMARY KEY NOT NULL,
  full_name TEXT NOT NULL
);

selectAll:
SELECT * FROM hockeyPlayer;

selectByNumber:
SELECT full_name FROM hockeyPlayer WHERE player_number = ?;

insert:
INSERT INTO hockeyPlayer(player_number, full_name) VALUES (?, ?);
```

ビルドすると、ファイルごとに`PlayerQueries`が生成され、ラベル名がそのまま関数名になります。

```
val driver: SqlDriver = AndroidSqliteDriver(Database.Schema, context, "test.db")
val database = Database(driver)
val playerQueries: PlayerQueries = database.playerQueries

playerQueries.insert(player_number = 10, full_name = "Corey Perry")
val all: List<HockeyPlayer> = playerQueries.selectAll().executeAsList()
val name: String? = playerQueries.selectByNumber(10).executeAsOneOrNull()
```

`AndroidSqliteDriver`はドライバの作成時にスキーマの作成とマイグレーションを自動で行います。2.1.0からは、生成されるinsert・update・deleteの関数が変更行数を返す仕様です。1.x系や2.0系から上げる場合、戻り値を`Unit`と決め打ちしている呼び出し側の修正が要ります。

### coroutines-extensionsのasFlowで変更を監視しUIへ流す書き方

画面に一覧を出し続けるなら、[公式のCoroutines拡張](https://sqldelight.github.io/sqldelight/latest/android%5Fsqlite/coroutines/)でクエリをFlowに変えます。対象テーブルにinsertやupdateが走るたびに、新しい結果が流れてきます。

```
val players: Flow<List<HockeyPlayer>> =
    playerQueries.selectAll()
        .asFlow()
        .mapToList(Dispatchers.IO)
```

ViewModelでは、このFlowを`stateIn`でStateFlowに変えてComposeへ渡すのが定番の形です。`mapToList`に渡すディスパッチャを省くとメインスレッドでクエリが走るため、必ずIOを指定します。suspendとディスパッチャの関係は[Kotlin Coroutinesとは？suspendの仕組みと構造化並行性・採用判断](https://www.issoh.co.jp/tech/details/16036/)で整理しています。

### AS構文とColumnAdapterでenumやListを列に保存する設定例

SQLiteにない型は、列定義の`AS`でKotlinの型を指定し、変換を`ColumnAdapter`に書きます。[型の公式ガイド](https://sqldelight.github.io/sqldelight/latest/android%5Fsqlite/types/)の例をもとにしたコードです。

```
-- Team.sq
import kotlin.collections.List;

CREATE TABLE team (
  name TEXT PRIMARY KEY NOT NULL,
  cup_wins TEXT AS List<String> NOT NULL,
  position TEXT AS com.example.Position NOT NULL
);

// Kotlin 側
val listOfStringsAdapter = object : ColumnAdapter<List<String>, String> {
    override fun decode(databaseValue: String) =
        if (databaseValue.isEmpty()) listOf() else databaseValue.split(",")
    override fun encode(value: List<String>) = value.joinToString(separator = ",")
}

val database = Database(
    driver = driver,
    teamAdapter = Team.Adapter(
        cup_winsAdapter = listOfStringsAdapter,
        positionAdapter = EnumColumnAdapter()
    )
)
```

enumはランタイム同梱の`EnumColumnAdapter`で名前の文字列として保存されます。enumの定数名を後から変えると既存行が読めなくなるため、名前を変える可能性がある値は、別の文字列コードを持たせて自前のアダプタで変換します。

## マイグレーション検証：.sqmファイルと検証タスクによる移行事故の防止

### 1.sqmから始まる番号付けとDatabase.Schema.versionの関係

[マイグレーションの公式ガイド](https://sqldelight.github.io/sqldelight/latest/android%5Fsqlite/migrations/)では、スキーマの初版を1とし、移行用のファイルを「移行元のバージョン番号.sqm」と名付けます。版2へ上げるSQLは`1.sqm`に書き、`.sq`と同じソースセットに置きます。

```
-- src/main/sqldelight/com/example/1.sqm（版1から版2へ）
ALTER TABLE hockeyPlayer ADD COLUMN team TEXT;
```

`.sqm`を1本足すと`Database.Schema.version`が1つ上がります。同時に`Player.sq`のCREATE TABLEにも`team`列を足すのを忘れないでください。新規インストールは`.sq`から、既存ユーザーは`.sqm`の積み重ねでスキーマが作られるため、両者がずれると端末によって列の有無が変わります。

### CIでマイグレーション検証を有効にする設定とスキーマ保存の手順

CIで使う設定は`schemaOutputDirectory`と`verifyMigrations`です。このずれをビルド時に止める仕組みが、`verifySqlDelightMigration`タスクです。過去の版のスキーマを`.db`ファイルとして保存しておき、そこにすべての`.sqm`を適用した結果が最新の`.sq`と一致するかを検証します。ただし`verifyMigrations`の既定値はfalse、`schemaOutputDirectory`の既定値はnullなので、明示しない限り検証は働きません。

```
sqldelight {
    databases {
        register("Database") {
            packageName.set("com.example")
            schemaOutputDirectory.set(file("src/main/sqldelight/databases"))
            verifyMigrations.set(true)
        }
    }
}

# リリースのたびに現行スキーマを .db として保存しコミットする
./gradlew tasks --all | grep -i schema
# 検証（check タスクにも含まれる）
./gradlew verifySqlDelightMigration
```

スキーマの保存は`generate<ソースセット名><データベース名>Schema`という名前のタスクで行います。リリース時に生成した`2.db`や`3.db`をリポジトリにコミットしておくのが運用の要点です。ここを省くと、検証タスクは比べる相手を持たず、版を飛ばして更新したユーザーだけが起動時にクラッシュする事故を防げません。

## Kotlin MultiplatformでのSQLDelightのドライバ構成

### commonMainのクエリ共有とAndroid・iOS・JVMのドライバ切り替え

KMPでは、`.sq`ファイルと生成コードを共通モジュールに置き、プラットフォームごとに違うのはドライバだけという構成になります。[マルチプラットフォーム向けの公式手順](https://sqldelight.github.io/sqldelight/latest/multiplatform%5Fsqlite/)では、ソースセットごとに次のドライバを追加します。SQLファイルの置き場所は`src/commonMain/sqldelight`です。

```
kotlin {
    sourceSets.androidMain.dependencies {
        implementation("app.cash.sqldelight:android-driver:2.4.1")
    }
    // iosMain や windowsMain でも可
    sourceSets.nativeMain.dependencies {
        implementation("app.cash.sqldelight:native-driver:2.4.1")
    }
    sourceSets.jvmMain.dependencies {
        implementation("app.cash.sqldelight:sqlite-driver:2.4.1")
    }
}
```

2.1.0からはwasmJs向けのWeb Workerドライバも用意されています。KMPそのものの仕組みや、FlutterやReact Nativeとの選び分けは[Kotlin Multiplatform（KMP）とは？仕組みとFlutter・React Native比較、導入判断](https://www.issoh.co.jp/tech/details/3310/)にまとめています。

### iOS・JVM向けドライバをexpect/actualで初期化するコード

iOSでは`NativeSqliteDriver`、JVMでは`JdbcSqliteDriver`を初期化します。ドライバの生成を分ける仕組みはexpect/actualです。共通側は`SqlDriver`を返す関数の宣言だけを持ちます。

```
// commonMain
expect class DriverFactory {
    fun createDriver(): SqlDriver
}
fun createDatabase(factory: DriverFactory): Database = Database(factory.createDriver())

// androidMain
actual class DriverFactory(private val context: Context) {
    actual fun createDriver(): SqlDriver =
        AndroidSqliteDriver(Database.Schema, context, "test.db")
}

// nativeMain（iOS）
actual class DriverFactory {
    actual fun createDriver(): SqlDriver =
        NativeSqliteDriver(Database.Schema, "test.db")
}

// jvmMain（デスクトップ）
actual class DriverFactory {
    actual fun createDriver(): SqlDriver =
        JdbcSqliteDriver("jdbc:sqlite:test.db", Properties(), Database.Schema)
}
```

Androidだけ`Context`を受け取る点が、DIの設計で考慮すべき違いです。KoinやHiltでDriverFactoryをプラットフォーム側のモジュールから注入し、共通コードは`Database`だけを受け取る形にしておくと、テストではJVMのインメモリDBに差し替えられます。UIまで共通化する場合の判断は[Compose Multiplatformとは？KMPとの違いとiOS対応の現在地・採用判断を解説](https://www.issoh.co.jp/tech/details/16032/)が参考になります。

## SQLDelightとRoomの選び分け：Room 3.0のKMP対応後に残る判断基準

### SQLDelight 2.4とRoom 2.8・Room 3.0の生成・対応環境比較

Roomは[2.7.0でKMPライブラリに作り替えられ](https://developer.android.com/jetpack/androidx/releases/room)、安定版は2026年9月9日公開の2.8.5です。[Room 3.0](https://developer.android.com/jetpack/androidx/releases/room3)は同日に3.0.3が安定版として出ており、パッケージが`androidx.room3`に変わったうえで、KSP必須・Kotlinのコード生成のみという構成になりました。

| 観点          | SQLDelight 2.4.1       | Room 2.8.5        | Room 3.0.3        |
| ----------- | ---------------------- | ----------------- | ----------------- |
| 正とするもの      | SQLファイル                | アノテーション付きクラス      | アノテーション付きクラス      |
| コード生成       | Gradleプラグイン            | KSPまたはKAPT        | KSPのみ             |
| KMPのターゲット   | Android・iOS・JVM・wasmJs | Android・iOS・JVM   | 左記＋JS・WasmJs      |
| SQLite以外の方言 | MySQL・PostgreSQL・HSQL  | なし                | なし                |
| マイグレーション    | .sqmファイル               | 自動移行＋Migrationクラス | 自動移行＋Migrationクラス |
| 非同期の前提      | 同期API＋Flow拡張           | 同期・suspend混在      | suspendかFlowが必須   |

[Room KMPの公式手順](https://developer.android.com/kotlin/multiplatform/room)では、Android以外のDAOはすべてsuspend関数にする必要があり、LiveDataは使えません。Room 2.x系のAndroidアプリをKMPへ広げる場合も、DAOの書き換えが先に発生します。

### SQLDelightを選ぶ条件：SQLを正とする設計とサーバー側との共有

SQLDelightを選ぶのは、次のどれかに当てはまる場合です。優先度の高い順に並べます。

- 複雑なJOINやウィンドウ関数、[FTS5の全文検索](https://www.issoh.co.jp/tech/details/9314/)など、SQLを手で書いて性能を詰めたい
- KtorなどのサーバーとアプリをKotlinで揃え、PostgreSQLやMySQLのクエリも同じ書き方で管理したい
- スキーマ変更をSQLのdiffでレビューし、`verifySqlDelightMigration`でCIに止めさせたい
- 既存のRoom 2.x資産がなく、Room 3.0のsuspend必須の制約を後から受けたくない

SQLが読めるメンバーが揃っていれば、生成コードを開かなくてもクエリの中身をレビューできます。これがSQLDelight最大の利点です。

### Roomで足りるため採用しない条件と、途中で乗り換えて詰まる失敗パターン

Androidだけのアプリで、データ量が数千行程度、クエリも単純な一覧と詳細だけなら、SQLDelightを入れる理由は薄いと判断します。Roomの公式ドキュメントとJetpackのサンプルに沿えば、Paging・WorkManagerとの組み合わせ例も揃っており、メンバーの入れ替わりにも強い構成になります。オブジェクトをそのまま保存したい要件なら[Realm（モバイルDB）とは？SQLiteとの違いとDevice Sync終了後の採用判断を解説](https://www.issoh.co.jp/tech/details/15808/)も確認してください。

避けたいのは、Roomで運用中のアプリを「KMPにするから」という理由だけでSQLDelightへ途中で乗り換えるケースです。Roomが作った既存のテーブルとバージョン番号に`.sq`と`.sqm`を合わせ込む必要があり、ユーザー端末のDBを壊さない移行の検証に大きな工数がかかります。いまならRoom 2.8からRoom 3.0へ上げる道もあるため、乗り換えの対象は、スキーマを作り直す大型改修のときに限定する方針です。既存アプリの永続化層の見直しやKMP化の工数を外部と相談したい場合は、[Androidアプリ開発](https://www.issoh.co.jp/service/apps/android/)のページで対応範囲を確認できます。

## よくある質問

SQLDelightを調べている開発者から出やすい質問に、公式ドキュメントの記述をもとに答えます。

### SQLDelightの読み方は何ですか？

英語の発音どおり「エスキューエルディライト」と読みます。SQLの部分を「シークェル」と読み「シークェルディライト」と呼ぶ人もいます。Gradleプラグインのidは`app.cash.sqldelight`で、ライブラリの説明文は「Generates typesafe Kotlin APIs from SQL」です。名前のとおり、SQLから型安全なKotlinのAPIを作ることが役割です。

### SQLDelightは無料で商用利用できますか？

Apache-2.0ライセンスのOSSなので、商用アプリに組み込んでも利用料はかかりません。配布物に含める場合は、ライセンス文の同梱など、Apache-2.0の表示義務に従います。Androidアプリなら、アプリ内のオープンソースライセンス一覧画面にSQLDelightのライセンス文を載せる形が一般的な対応です。IDEプラグインも無償で配布されています。

### SQLDelightとRoomは同じプロジェクトで併用できますか？

別々のデータベースファイルを扱うなら併用できます。ただし同じファイルを両方から開くと、スキーマのバージョン管理が衝突する構成です。Roomは自前の管理テーブルとバージョン番号を持ち、SQLDelightは`.sqm`の数で版を決めるため、どちらかが相手の変更を壊します。1つのDBにはどちらか一方を使う方針にしてください。

### SQLDelightでPostgreSQLやMySQLも使えますか？

使えます。Gradleの`dialect`にPostgreSQLやMySQLの方言モジュールを指定すると、その方言の構文でSQLが検証されます。JVMのサーバーでJDBCやR2DBCのドライバと組み合わせる用途です。モバイルアプリから直接接続する使い方は想定されていません。2.4.0のリリースノートでも、PostgreSQL・MySQLの構文対応が多数追加されています。

### SQLDelightの1.x系から2.x系への移行で何が変わりますか？

大きいのはMavenの座標とパッケージ名で、`com.squareup.sqldelight`から`app.cash.sqldelight`に変わりました。Gradleプラグインのidとimport文をまとめて置き換える必要があります。2.1.0以降はinsertなどの戻り値が変更行数になり、2.3.2でAndroidドライバのminSdkは23に上がっています。段階を踏む場合も、最終的には2.4.1の公式手順に合わせ、マイグレーション検証を有効にした状態で上げてください。

## 関連記事

- [Kotlin Multiplatform（KMP）とは？仕組みとFlutter・React Native比較、導入判断](https://www.issoh.co.jp/tech/details/3310/)：SQLDelightを共通モジュールに置く前提となるKMPの全体像です
- [Kotlinとは？Javaとの違いとK2コンパイラ・2.4系の現在地を実装者目線で解説](https://www.issoh.co.jp/tech/details/15789/)：生成コードが前提とするKotlinの現行版を確認できます
- [Kotlin Coroutinesとは？suspendの仕組みと構造化並行性・採用判断](https://www.issoh.co.jp/tech/details/16036/)：asFlowとRoom 3.0のsuspend必須を理解する土台になります
- [Realm（モバイルDB）とは？SQLiteとの違いとDevice Sync終了後の採用判断を解説](https://www.issoh.co.jp/tech/details/15808/)：SQLiteを使わないモバイルDBという別の選択肢です
- [Androidアプリ開発の言語はKotlinとJavaのどちらか？選定基準と環境まで解説](https://www.issoh.co.jp/tech/details/15321/)：Room 3.0でJavaのコード生成が無くなった影響の判断材料になります

---

出典: [SQLDelightとは：SQLから型安全なKotlinコードを生成する仕組みとRoomとの選び分け【2.4系】](<https://www.issoh.co.jp/tech/details/19198/>)（株式会社一創）
