Database Dialect Support
Built-in Database Dialects
Kronos chooses the SQL dialect and table-structure statements from KronosDataSourceWrapper.dbType.
val wrapper = KronosJdbcWrapper(dataSource)
Kronos.dataSource = { wrapper }
println(wrapper.dbType)
println(wrapper.sqlDialect)The complete built-in dialects map to these DBType values:
| Database | DBType |
|---|---|
| MySQL | DBType.Mysql |
| PostgreSQL | DBType.Postgres |
| SQLite | DBType.SQLite |
| H2 | DBType.H2 |
| SQL Server | DBType.Mssql |
| Oracle | DBType.Oracle |
| DM8 (Dameng) | DBType.DM8 |
For database connection and JDBC driver examples, see
📚Connect to DB. For table DDL workflows, see📚Schema Syncand📚Create Table As Select.
Behavior matrix
Use this page as the dialect behavior matrix. Each row links to the section or page that shows the Kotlin entry point and the SQL shape for the built-in dialects.
| Behavior | Where to check |
|---|---|
| Pagination | See Pagination for page(pageIndex, pageSize) SQL. |
Upsert and onConflict() | See Upsert and 📚Upsert for match-field upsert, uniqueness-conflict upsert, and dynamic conflict assignments. |
| Last insert id | See Last Insert Id and 📚Last Insert Id for identity key retrieval. |
| H2 and DM8 | See the dedicated sections below for H2 MERGE upserts, DM8 identity columns, and JDBC-generated identity keys. |
| DDL and schema sync | See Table Operations, 📚Table Operations, and 📚Schema Sync. |
| Column type rendering | See 📚Column Types for KColumnType examples and dialect output. |
| Identifier quoting | See Identifier Quoting for table and column quoting rules. |
| Functions and windows | See Built-in Functions, 📚Functions, and 📚Sorting, Pagination, and Aggregation. |
Identifier Quoting
The same query uses the identifier quoting rules of the active dialect.
kotlin
val users = User()
.select { [it.id, it.name] }
.toList()Mysql
SELECT `id`, `name`
FROM `user`PostgreSQL
SELECT "id", "name"
FROM "user"SQLite
SELECT "id", "name"
FROM "user"SQLServer
SELECT [id], [name]
FROM [user]Oracle
SELECT "ID", "NAME"
FROM "USER"Pagination
page(pageIndex, pageSize) starts from page 1. Add withTotal() after page(...) when a named PageResult with total metadata is required. Kronos renders the matching pagination syntax for the active database.
kotlin
val page = User()
.select { [it.id, it.name] }
.orderBy { it.id.asc() }
.page(2, 20)
.withTotal()
.toList()
val users = page.recordsMysql
SELECT `id`, `name`
FROM `user`
ORDER BY `id` ASC
LIMIT 20 OFFSET 20PostgreSQL
SELECT "id", "name"
FROM "user"
ORDER BY "id" ASC
LIMIT 20 OFFSET 20SQLite
SELECT "id", "name"
FROM "user"
ORDER BY "id" ASC
LIMIT 20 OFFSET 20SQLServer
SELECT [id], [name]
FROM [user]
ORDER BY [id] ASC
OFFSET 20 ROWS FETCH NEXT 20 ROWS ONLYOracle
SELECT "ID", "NAME"
FROM "USER"
ORDER BY "ID" ASC
OFFSET 20 ROWS FETCH NEXT 20 ROWS ONLYUpsert
onConflict() uses database uniqueness conflicts and renders the matching SQL for the active dialect. When on { ... } is omitted, Kronos infers the conflict target from KPojo uniqueness metadata: primary-key values and declared unique indexes.
kotlin
User(id = 1, name = "Ada")
.upsert { it.name }
.onConflict()
.execute()Mysql
INSERT INTO `user` (`id`, `name`) VALUES (:id, :name)
ON DUPLICATE KEY UPDATE `name` = :namePostgreSQL
INSERT INTO "user" ("id", "name") VALUES (:id, :name)
ON CONFLICT ("id") DO UPDATE SET "name" = :nameSQLite
INSERT INTO "user" ("id", "name") VALUES (:id, :name)
ON CONFLICT ("id") DO UPDATE SET "name" = :nameSQLServer
MERGE INTO [user] AS [t1]
USING (SELECT :id AS [id], :name AS [name]) AS [t2]
ON ([t1].[id] = [t2].[id])
WHEN MATCHED THEN UPDATE SET [t1].[name] = :name
WHEN NOT MATCHED THEN INSERT ([id], [name]) VALUES (:id, :name)Oracle
MERGE INTO "USER" "T1"
USING (SELECT :id AS "ID", :name AS "NAME") "T2"
ON ("T1"."ID" = "T2"."ID")
WHEN MATCHED THEN UPDATE SET "T1"."NAME" = :name
WHEN NOT MATCHED THEN INSERT ("ID", "NAME") VALUES (:id, :name)Last Insert Id
An insert on an identity primary key can read lastInsertId when the insert uses .withId(). The built-in KronosJdbcWrapper reads JDBC generated keys during insert execution; wrappers that use a follow-up query use the dialect SQL below.
kotlin
val id = User(name = "Kronos")
.insert()
.withId()
.execute()
.lastInsertIdMysql
SELECT LAST_INSERT_ID()PostgreSQL
SELECT LASTVAL()SQLite
SELECT last_insert_rowid()SQLServer
SELECT SCOPE_IDENTITY()Oracle
SELECT MAX("ID") FROM "USER"Table Operations
table.createTable(...), table.syncTable(...), table.dropTable(...), and table.truncateTable(...) use the active database's DatabaseStatements.
kotlin
val table = Kronos.dataSource.table
table.createTable(Account())
table.syncTable(Account())
table.truncateTable(Account())
table.dropTable(Account())Mysql
CREATE TABLE `account` (`id` SIGNED NOT NULL, `name` CHAR(64), `created_at` DATETIME)PostgreSQL
CREATE TABLE "account" ("id" INTEGER NOT NULL, "name" VARCHAR(64), "created_at" TIMESTAMP)SQLite
CREATE TABLE "account" ("id" INTEGER NOT NULL, "name" VARCHAR(64), "created_at" TIMESTAMP)SQLServer
CREATE TABLE [account] ([name] NVARCHAR(32), [created_at] DATETIME2)Oracle
CREATE TABLE "ACCOUNT" ("NAME" VARCHAR(4000), "SCORE" INT)Schema sync reads the current table structure first, then executes the required DDL.
kotlin
val changed = Kronos.dataSource.table.syncTable(User())result
changed == false // Kronos creates the target table
changed == true // Kronos synchronizes the existing table by diffBuilt-in Functions
The function DSL keeps the same Kotlin call shape while the active dialect selects its SQL form. Function availability and argument semantics are covered in 📚Functions.
kotlin
val rows = User()
.select { f.rand().alias("rand") }
.toList()Mysql
SELECT RAND() AS rand
FROM `tb_user`PostgreSQL
SELECT RANDOM() AS rand
FROM "tb_user"SQLite
SELECT (RANDOM() / 9223372036854775808.0 + 0.5) AS rand
FROM "tb_user"H2
SELECT RAND() AS rand
FROM "tb_user"SQLServer
SELECT RAND() AS rand
FROM [tb_user]Oracle
SELECT DBMS_RANDOM.VALUE AS RAND
FROM "TB_USER"DM8
SELECT DBMS_RANDOM.VALUE AS RAND
FROM "TB_USER"H2
H2 uses quoted identifiers and H2 MERGE syntax for conflict-based upserts. Use the H2 JDBC configuration in 📚Connect to DB for an in-memory or server connection.
kotlin
User(id = 1, name = "Ada")
.upsert { it.name }
.onConflict()
.execute()H2
MERGE INTO "user" AS "t1"
USING (VALUES (:id, :name)) AS "t2" ("id", "name")
ON ("t1"."id" = "t2"."id")
WHEN MATCHED THEN UPDATE SET "name" = "t2"."name"
WHEN NOT MATCHED THEN INSERT ("id", "name") VALUES ("t2"."id", "t2"."name")H2 identity fields use H2 identity columns. Insert tasks with .withId() return the JDBC-generated key.
DM8
DM8 uses Oracle-compatible query syntax and DM8-native identity columns. Configure its JDBC driver as shown in 📚Connect to DB.
kotlin
@PrimaryKey(identity = true)
var id: Int? = nullKronos renders this field as INT IDENTITY(1,1) in DM8 table DDL. Insert tasks with .withId() return the JDBC-generated key.
Database Enum Values and Extension
DBType also contains DB2, Sybase, OceanBase, and GaussDB for connection identification and dialect extension entry points.
val dbType = DBType.fromName(connection.metaData.databaseProductName)For adding a new database dialect, see 📚Create Database Dialect.
For custom database connections, see 📚concept - data-source-wrapper.