数据源包装器
KronosDataSourceWrapper连接Kronos SQL task和数据库执行层。内置JDBC实现是KronosJdbcWrapper,自定义wrapper可以把执行委托给Spring JDBC、JDBI、MyBatis或项目中的数据访问层。
Native 驱动
为平台驱动实现 KronosExecutionDataSource,再通过 KronosExecutionWrapper 执行共享 ORM 查询。完整连接示例见 📚Native SQLite。
配置默认数据源
Kronos.connect(...)配置Kronos.dataSource,并返回KronosJdbcWrapper。
kotlin
import com.kotlinorm.Kronos
import com.kotlinorm.connect
Kronos.connect(
url = "jdbc:postgresql://localhost:5432/kronos",
userName = "postgres",
password = "******",
driverClassName = "org.postgresql.Driver"
)方言需要显式指定时传入databaseType,JDBC wrapper设置放在配置block中。
创建多个数据源的wrapper
读写分离或按租户选择数据源时,可以用DriverManagerDataSource创建独立wrapper。
kotlin
import com.kotlinorm.enums.DBType
import com.kotlinorm.wrappers.DriverManagerDataSource
import com.kotlinorm.wrappers.KronosJdbcWrapper
val reportingWrapper = KronosJdbcWrapper(
dataSource = DriverManagerDataSource(
url = "jdbc:postgresql://localhost:5432/kronos",
userName = "postgres",
password = "******",
driverClassName = "org.postgresql.Driver"
),
databaseType = DBType.Postgres
)事务需要固定使用该数据源时,调用Kronos.transact(reportingWrapper)。应用服务使用连接池时,添加连接池和JDBC Driver后,将连接池的DataSource传给KronosJdbcWrapper。
Android SQLite
Android/JVM SQLiteDatabase 配置和完整 wrapper 参考实现见📚Android SQLite。
成员属性
| 名称 | 说明 | 类型 |
|---|---|---|
url | 数据库连接URL | String |
userName | 数据库用户名 | String |
dbType | SQL渲染和DDL statement使用的数据库类型 | DBType |
sqlDialect | 由dbType解析出的SQL方言 | SqlDialect |
创建wrapper后可以读取这些属性。
kotlin
val wrapper = KronosJdbcWrapper(dataSource)
println(wrapper.url)
println(wrapper.userName)
println(wrapper.dbType)
println(wrapper.sqlDialect.family)output
jdbc:postgresql://localhost:5432/kronos
postgres
Postgres
PostgreSql配置内置JDBC wrapper
KronosJdbcWrapper接收DataSource,包括DriverManagerDataSource或连接池数据源、可选的DBType和配置block。需要显式指定方言时,可以传入databaseType。
database type
import com.kotlinorm.enums.DBType
import com.kotlinorm.wrappers.KronosJdbcWrapper
val wrapper = KronosJdbcWrapper(
dataSource = dataSource,
databaseType = DBType.Mysql
)JDBC statement设置和SQL warning处理可以放在配置block里。
statement
import com.kotlinorm.wrappers.KronosDuplicateKeyException
import com.kotlinorm.wrappers.KronosJdbcWrapper
import com.kotlinorm.wrappers.KronosSqlWarningPolicy
import com.kotlinorm.wrappers.SqlStateSQLExceptionTranslator
val wrapper = KronosJdbcWrapper(dataSource) {
statement.fetchSize = 1000
statement.maxRows = 5000
statement.queryTimeoutSeconds = 30
warningPolicy = KronosSqlWarningPolicy.THROW
exceptionTranslator = { sql, params, exception ->
if (exception.errorCode == 1062) {
KronosDuplicateKeyException("Duplicate key", sql, params, exception)
} else {
SqlStateSQLExceptionTranslator().translate(sql, params, exception)
}
}
}内置wrapper也提供自定义参数绑定和结果映射注册入口。
mapping
import com.kotlinorm.wrappers.KronosArgument
import com.kotlinorm.wrappers.KronosArgumentFactory
import com.kotlinorm.wrappers.KronosJdbcWrapper
import com.kotlinorm.wrappers.KronosPhysicalReadResult
import com.kotlinorm.wrappers.KronosPhysicalValueReader
import java.sql.Types
import java.util.UUID
val wrapper = KronosJdbcWrapper(dataSource) {
arguments.register(KronosArgumentFactory { value, _ ->
if (value is UUID) {
KronosArgument { position, statement, _ ->
statement.setObject(position, value, Types.OTHER)
}
} else {
null
}
})
columnMappers.register(KronosPhysicalValueReader { resultSet, position, _ ->
val jdbcTypeName = resultSet.metaData.getColumnTypeName(position)
if (jdbcTypeName.equals("jsonb", ignoreCase = true)) {
KronosPhysicalReadResult.Handled(resultSet.getString(position))
} else {
KronosPhysicalReadResult.NotHandled
}
})
}物理 reader 会在ResultSet.getObject(position)之前检查 JDBC metadata 或 driver 行为。已经处理且值为 SQL NULL 时返回Handled(null);需要继续默认 JDBC 读取时返回NotHandled。读取到的字符串需要转换为 UUID 等领域值时,注册一份自定义值映射。
使用toList(task) 查询列表
toList(task)执行select task,并根据task.targetType映射每一行。KType会保留泛型参数和可空性,因此wrapper不再需要额外接收KClass、isKPojo或父类型参数。
signature
fun toList(task: KAtomicQueryTask): List<Any?>kotlin
val users = wrapper.toList(
KronosAtomicQueryTask(
sql = "SELECT id, name FROM user WHERE age > :age",
paramMap = mapOf("age" to 18),
targetType = typeOf<User>()
)
)内置 JDBC wrapper 会根据目标 KType 识别标量、typed row Map 和 KPojo。每行 JDBC 结果都会物化为 LinkedHashMap,因此只支持直接声明的 Map 和 MutableMap(顶层目标可空也支持)。固定实现、调整泛型顺序或自定义 Map 子类型都会被拒绝,因为 Kronos 无法安全返回声明的容器。Map key 只支持 String、String? 或 *;Any、Any? 和 * value 保持 raw,具体 value 参数则使用完整 KType 转换。
使用first(task) 查询单个值
first(task)使用相同的目标类型,空结果返回null。上层first<T>()会根据请求类型是否可空决定空结果是否抛出异常。
signature
fun first(task: KAtomicQueryTask): Any?kotlin
val user = wrapper.first(
KronosAtomicQueryTask(
sql = "SELECT id, name FROM user WHERE id = :id",
paramMap = mapOf("id" to 1),
targetType = typeOf<User?>()
)
)使用update(task) 执行写入
update(task)执行insert、update、delete和DDL task,并返回影响行数。
signature
fun update(task: KAtomicActionTask): Intkotlin
val affectedRows = wrapper.update(
KronosAtomicActionTask(
sql = "UPDATE user SET name = :name WHERE id = :id",
paramMap = mapOf("name" to "Ada", "id" to 1)
)
)result
1使用batchUpdate(task) 执行批量写入
batchUpdate(task)用多组参数执行同一条SQL,并返回每个批次项的JDBC update count。
signature
fun batchUpdate(task: KronosAtomicBatchTask): IntArraykotlin
val affectedRows = wrapper.batchUpdate(
KronosAtomicBatchTask(
sql = "UPDATE user SET name = :name WHERE id = :id",
paramMapArr = arrayOf(
mapOf("name" to "Ada", "id" to 1),
mapOf("name" to "Linus", "id" to 2)
)
)
)result
[1, 1]使用transact 执行事务
transact(...)用TransactionScope作为block receiver。JDBC wrapper可以在block中使用savepoint、rollbackToSavepoint和releaseSavepoint。
signature
fun transact(
isolation: TransactionIsolation? = null,
timeout: Int? = null,
block: TransactionScope.() -> Any?
): Any?kotlin
val result = wrapper.transact(
isolation = TransactionIsolation.READ_COMMITTED,
timeout = 30
) {
val point = savepoint("before_user_update")
try {
wrapper.update(
KronosAtomicActionTask(
sql = "UPDATE user SET name = :name WHERE id = :id",
paramMap = mapOf("name" to "Ada", "id" to 1)
)
)
releaseSavepoint(point)
} catch (e: Exception) {
rollbackToSavepoint(point)
throw e
}
}转换命名参数
task.parsed()把:name参数转换为JDBC ?占位符。KronosAtomicBatchTask.parsedArr()为每个批次项返回同样的结构。
query task
val task = KronosAtomicQueryTask(
sql = "SELECT id, name FROM user WHERE id = :id AND status = :status",
paramMap = mapOf("id" to 1, "status" to "ACTIVE"),
targetType = typeOf<Map<String, Any?>>()
)
val parsed = task.parsed()
println(parsed.jdbcSql)
println(parsed.jdbcParamList.toList())output
SELECT id, name FROM user WHERE id = ? AND status = ?
[1, ACTIVE]batch task
val batchTask = KronosAtomicBatchTask(
sql = "UPDATE user SET status = :status WHERE id = :id",
paramMapArr = arrayOf(
mapOf("status" to "ACTIVE", "id" to 1),
mapOf("status" to "LOCKED", "id" to 2)
)
)
val (jdbcSql, batchParams) = batchTask.parsedArr()
println(jdbcSql)
println(batchParams)batch output
UPDATE user SET status = ? WHERE id = ?
[[ACTIVE, 1], [LOCKED, 2]]Spring JDBC wrapper示例见📚数据源及三方框架扩展。