
Enhances SQLDelight with a driver wrapping AndroidX SQLite libraries, simplifying database management via the AndroidxSqliteDriver factory, handling migrations seamlessly.
sqldelight-androidx-driver provides a SQLDelight SqlDriver that wraps the AndroidX Kotlin Multiplatform SQLite
libraries.
It works with any of the available implementations of AndroidX SQLite; see their documentation for more information.
repositories {
mavenCentral()
}
dependencies {
implementation("com.eygraber:sqldelight-androidx-driver:0.0.17")
}Snapshots can be found here.
Assuming the following configuration:
sqldelight {
databases {
create("Database")
}
}you get started by creating a AndroidxSqliteDriver:
Database(
AndroidxSqliteDriver(
driver = BundledSQLiteDriver(),
databaseType = AndroidxSqliteDatabaseType.File("<absolute path to db file>"),
schema = Database.Schema,
)
)on Android and JVM you can pass a File:
Database(
AndroidxSqliteDriver(
driver = BundledSQLiteDriver(),
databaseType = AndroidxSqliteDatabaseType.File(File("my.db")),
schema = Database.Schema,
)
)and on Android you can pass a Context to create the file in the app's database directory:
Database(
AndroidxSqliteDriver(
driver = BundledSQLiteDriver(),
databaseType = AndroidxSqliteDatabaseType.FileProvider(context, "my.db"),
schema = Database.Schema,
)
)If you want to provide OpenFlags to the bundled or native driver, you can use:
Database(
AndroidxSqliteDriver(
connectionFactory = object : AndroidxConnectionFactory {
override val driver = BundledSQLiteDriver()
override fun createConnection(name: String) =
driver.open(name, SQLITE_OPEN_READWRITE or SQLITE_OPEN_CREATE)
},
databaseType = AndroidxSqliteDatabaseType.File("<absolute path to db file>"),
schema = Database.Schema,
)
)It will handle calling the create and migrate functions on your schema for you, and keep track of the database's version.
When using AndroidxSqliteDriver, the handling of foreign key constraints during database creation and migration is
managed to ensure data integrity.
If you have foreign key constraints enabled in your
AndroidxSqliteConfiguration (i.e. isForeignKeyConstraintsEnabled = true),
the driver will automatically disable them before executing the schema create or migrate operations.
This is done to prevent issues with table creation order and data manipulation during the migration process.
After the creation or migration is complete, foreign key constraints are re-enabled.
Furthermore, to verify the integrity of the foreign key relationships after these operations,
the driver performs an additional check. If isForeignKeyConstraintsCheckedAfterCreateOrUpdate
is true (which it is by default), a PRAGMA foreign_key_check is executed. If this check finds
any violations, an AndroidxSqliteDriver.ForeignKeyConstraintCheckException is thrown, detailing the
specific constraints that have been violated. This helps catch any inconsistencies in your data that might
have been introduced during the migration.
[!IMPORTANT]
By default, the first 100 violations will be parsed out of the result set ofPRAGMA foreign_key_checkand stored in theAndroidxSqliteDriver.ForeignKeyConstraintCheckException. If your use can result in a large number of violations you can adjust the max amount that will be processed viaAndroidxSqliteConfiguration.maxMigrationForeignKeyConstraintViolationsToReport.
SQLite supports several concurrency models that can significantly impact your application's performance. This driver
provides flexible connection pooling through the AndroidxSqliteConcurrencyModel interface.
The simplest model with one connection handling all operations:
AndroidxSqliteConfiguration(
concurrencyModel = AndroidxSqliteConcurrencyModel.SingleReaderWriter
)Best for:
Dedicated reader connections for read-only access:
AndroidxSqliteConfiguration(
concurrencyModel = AndroidxSqliteConcurrencyModel.MultipleReaders(
readerCount = 3 // Number of concurrent reader connections
)
)Best for:
Important: This model is designed for read-only access. No write operations (INSERT, UPDATE, DELETE) should be
performed. If you need write capabilities, use MultipleReadersSingleWriter in WAL mode instead.
The most flexible model that adapts based on journal mode:
AndroidxSqliteConfiguration(
concurrencyModel = AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true, // Enable WAL mode for true concurrency
walCount = 4, // Reader connections when WAL is enabled
nonWalCount = 0 // Reader connections when WAL is disabled
)
)Best for:
PRAGMA synchronous = FULL is used)The optimal number of reader connections depends on your use case:
// Conservative (default)
AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true,
walCount = 4,
nonWalCount = 0,
)
// High-concurrency applications
AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true,
walCount = 8
)
// Memory-conscious applications
AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true,
walCount = 2
)On Android, you can use system-determined connection pool sizes:
// Based on SQLiteGlobal.getWALConnectionPoolSize()
fun getWALConnectionPoolSize(): Int {
val resources = Resources.getSystem()
val resId = resources.getIdentifier("db_connection_pool_size", "integer", "android")
return if (resId != 0) {
resources.getInteger(resId)
} else {
2 // Fallback default
}
}
AndroidxSqliteConfiguration(
concurrencyModel = AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true,
walCount = getWALConnectionPoolSize(),
nonWalCount = 0,
)
)| Model | Memory Usage | Read Concurrency | Write Capability | Best Use Case |
|---|---|---|---|---|
| SingleReaderWriter | Lowest | None | Full | Simple apps |
| MultipleReaders | Medium | Excellent | None (read-only) | Read-only apps |
| MultipleReadersSingleWriter (WAL) | Higher | Excellent | Full | Production |
| MultipleReadersSingleWriter (non-WAL) | Medium | Limited | Full | Legacy/constrained |
[!NOTE]
In-Memory and temporary databases automatically useSingleReaderWritermodel regardless of configuration, as connection pooling provides no benefit for these database types.
If PRAGMA journal_mode = ... is used, the connection pool will:
PRAGMA statementThis ensures all connections use the same journal mode and prevents inconsistencies.
MultipleReadersSingleWriter in WAL modeSee WAL & Dispatchers for more information about how to configure dispatchers to use for reads and writes.
sqldelight-androidx-driver provides a SQLDelight SqlDriver that wraps the AndroidX Kotlin Multiplatform SQLite
libraries.
It works with any of the available implementations of AndroidX SQLite; see their documentation for more information.
repositories {
mavenCentral()
}
dependencies {
implementation("com.eygraber:sqldelight-androidx-driver:0.0.17")
}Snapshots can be found here.
Assuming the following configuration:
sqldelight {
databases {
create("Database")
}
}you get started by creating a AndroidxSqliteDriver:
Database(
AndroidxSqliteDriver(
driver = BundledSQLiteDriver(),
databaseType = AndroidxSqliteDatabaseType.File("<absolute path to db file>"),
schema = Database.Schema,
)
)on Android and JVM you can pass a File:
Database(
AndroidxSqliteDriver(
driver = BundledSQLiteDriver(),
databaseType = AndroidxSqliteDatabaseType.File(File("my.db")),
schema = Database.Schema,
)
)and on Android you can pass a Context to create the file in the app's database directory:
Database(
AndroidxSqliteDriver(
driver = BundledSQLiteDriver(),
databaseType = AndroidxSqliteDatabaseType.FileProvider(context, "my.db"),
schema = Database.Schema,
)
)If you want to provide OpenFlags to the bundled or native driver, you can use:
Database(
AndroidxSqliteDriver(
connectionFactory = object : AndroidxConnectionFactory {
override val driver = BundledSQLiteDriver()
override fun createConnection(name: String) =
driver.open(name, SQLITE_OPEN_READWRITE or SQLITE_OPEN_CREATE)
},
databaseType = AndroidxSqliteDatabaseType.File("<absolute path to db file>"),
schema = Database.Schema,
)
)It will handle calling the create and migrate functions on your schema for you, and keep track of the database's version.
When using AndroidxSqliteDriver, the handling of foreign key constraints during database creation and migration is
managed to ensure data integrity.
If you have foreign key constraints enabled in your
AndroidxSqliteConfiguration (i.e. isForeignKeyConstraintsEnabled = true),
the driver will automatically disable them before executing the schema create or migrate operations.
This is done to prevent issues with table creation order and data manipulation during the migration process.
After the creation or migration is complete, foreign key constraints are re-enabled.
Furthermore, to verify the integrity of the foreign key relationships after these operations,
the driver performs an additional check. If isForeignKeyConstraintsCheckedAfterCreateOrUpdate
is true (which it is by default), a PRAGMA foreign_key_check is executed. If this check finds
any violations, an AndroidxSqliteDriver.ForeignKeyConstraintCheckException is thrown, detailing the
specific constraints that have been violated. This helps catch any inconsistencies in your data that might
have been introduced during the migration.
[!IMPORTANT]
By default, the first 100 violations will be parsed out of the result set ofPRAGMA foreign_key_checkand stored in theAndroidxSqliteDriver.ForeignKeyConstraintCheckException. If your use can result in a large number of violations you can adjust the max amount that will be processed viaAndroidxSqliteConfiguration.maxMigrationForeignKeyConstraintViolationsToReport.
SQLite supports several concurrency models that can significantly impact your application's performance. This driver
provides flexible connection pooling through the AndroidxSqliteConcurrencyModel interface.
The simplest model with one connection handling all operations:
AndroidxSqliteConfiguration(
concurrencyModel = AndroidxSqliteConcurrencyModel.SingleReaderWriter
)Best for:
Dedicated reader connections for read-only access:
AndroidxSqliteConfiguration(
concurrencyModel = AndroidxSqliteConcurrencyModel.MultipleReaders(
readerCount = 3 // Number of concurrent reader connections
)
)Best for:
Important: This model is designed for read-only access. No write operations (INSERT, UPDATE, DELETE) should be
performed. If you need write capabilities, use MultipleReadersSingleWriter in WAL mode instead.
The most flexible model that adapts based on journal mode:
AndroidxSqliteConfiguration(
concurrencyModel = AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true, // Enable WAL mode for true concurrency
walCount = 4, // Reader connections when WAL is enabled
nonWalCount = 0 // Reader connections when WAL is disabled
)
)Best for:
PRAGMA synchronous = FULL is used)The optimal number of reader connections depends on your use case:
// Conservative (default)
AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true,
walCount = 4,
nonWalCount = 0,
)
// High-concurrency applications
AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true,
walCount = 8
)
// Memory-conscious applications
AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true,
walCount = 2
)On Android, you can use system-determined connection pool sizes:
// Based on SQLiteGlobal.getWALConnectionPoolSize()
fun getWALConnectionPoolSize(): Int {
val resources = Resources.getSystem()
val resId = resources.getIdentifier("db_connection_pool_size", "integer", "android")
return if (resId != 0) {
resources.getInteger(resId)
} else {
2 // Fallback default
}
}
AndroidxSqliteConfiguration(
concurrencyModel = AndroidxSqliteConcurrencyModel.MultipleReadersSingleWriter(
isWal = true,
walCount = getWALConnectionPoolSize(),
nonWalCount = 0,
)
)| Model | Memory Usage | Read Concurrency | Write Capability | Best Use Case |
|---|---|---|---|---|
| SingleReaderWriter | Lowest | None | Full | Simple apps |
| MultipleReaders | Medium | Excellent | None (read-only) | Read-only apps |
| MultipleReadersSingleWriter (WAL) | Higher | Excellent | Full | Production |
| MultipleReadersSingleWriter (non-WAL) | Medium | Limited | Full | Legacy/constrained |
[!NOTE]
In-Memory and temporary databases automatically useSingleReaderWritermodel regardless of configuration, as connection pooling provides no benefit for these database types.
If PRAGMA journal_mode = ... is used, the connection pool will:
PRAGMA statementThis ensures all connections use the same journal mode and prevents inconsistencies.
MultipleReadersSingleWriter in WAL modeSee WAL & Dispatchers for more information about how to configure dispatchers to use for reads and writes.