@Database(
version = 1,
foreignKeyConstraintsEnforced = true,
tables = [User::class, Post::class],
views = [AuthorView::class],
queries = [UserNameQuery::class],
migrations = [AddEmailMigration::class],
)
abstract class AppDatabase : DBFlowDatabase<AppDatabase>() {
abstract val userAdapter: ModelAdapter<User>
abstract val postAdapter: ModelAdapter<Post>
abstract val authorViewAdapter: ViewAdapter<AuthorView>
abstract val userNameQueryAdapter: QueryAdapter<UserNameQuery>
}The generated type is AppDatabase_Database. Its companion implements DBCreator<AppDatabase>.
Default file name is the class name plus .db (AppDatabase.db). Override in settings.
A table belongs to one database. List it on @Database (preferred) or set @Table(database = AppDatabase::class).
Call createDB — the compiler plugin rewrites it to the generated factory. Works from commonMain; the Gradle plugin generates sources before compilation.
// Android
val db = createDB<AppDatabase>(context) {
copy(name = "App", inMemory = false)
}
// JVM / Native
val db = createDB<AppDatabase> {
copy(name = "App")
}
// Explicit platform settings (all targets)
val db = createDB<AppDatabase>(DBPlatformSettings()) {
copy(name = "App")
}createDB registers generated adapters (User.Companion as ModelAdapter<User>, and
the same for views and query models) so KClass lookups such as select from User::class work after open. select from User uses the table companion directly
and does not need a lookup.
DBSettings fields you typically copy:
| Field | Default | Purpose |
|---|---|---|
name |
database class name | File stem (must match [A-Za-z_$][A-Za-z0-9_$]*) |
inMemory |
false |
Tests / short-lived DBs |
databaseExtensionName |
".db" |
File suffix |
journalMode |
Automatic |
WAL on capable devices |
openHelperCreator |
platform SQLite | SQLCipher or a fake in tests |
databaseCallback |
null |
Open / upgrade hooks |
throwExceptionsOnCreate |
true |
Fail fast on create errors |
The first access to writableDatabase opens the file and runs migrations.
db.use {
writableDatabase // force open
}close() stops the dispatcher and closes the connection. destroy() also deletes the file.
db.writableTransaction {
userAdapter.save(user)
}
db.readableTransaction {
userAdapter.select().list()
}Both hop to transactionCoroutineDispatcher (single-thread executor by default). Nested work on the same database stays on that dispatcher.
Callbacks (async Transaction success / error) use callbackDispatcher (Dispatchers.Main on Android and JVM).
| Target | Open helper | Notes |
|---|---|---|
| Android | AndroidSQLiteOpenHelper |
create(context) needs Context |
| JVM | JDBC + sqlite-jdbc | create { } |
| Native | sqliter | Link -lsqlite3 |
Declare a second @Database and create it the same way. Do not share @Table types across databases.