数据源及三方框架扩展
Kronos通过自定义创建继承KronosDataSourceWrapper接口的包装类,可以轻松与第三方框架结合使用。
Native 驱动
为平台驱动实现 KronosExecutionDataSource,再通过 KronosExecutionWrapper 执行共享 ORM 查询。完整连接示例见 📚Native SQLite。
为现有框架实现wrapper
KronosDataSourceWrapper让Kronos使用项目中已有的数据库执行层。wrapper接收Kronos SQL task,并把它们委托给框架的query、update、batch和transaction API。
完整接口说明见📚数据源包装器。
Android SQLite 参考实现
独立的 📚Android SQLite 章节提供 Android/JVM SQLiteDatabase 配置和完整 wrapper 参考实现。
添加Spring JDBC和Kronos
Spring Boot项目通常由选定的Boot版本管理Spring依赖版本。JDBC Driver使用与数据库服务端、JDK匹配的最新稳定版。
maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.kotlinorm</groupId>
<artifactId>kronos-core</artifactId>
<version>0.4.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql-connector-j.version}</version>
</dependency>
</dependencies>gradle(kts)
dependencies {
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("com.kotlinorm:kronos-core:0.4.0")
implementation("com.mysql:mysql-connector-j:<latest-stable>")
}从DataSource 读取元信息
wrapper从JDBC元信息中提供url、userName和dbType。Kronos使用dbType选择SQL渲染和表结构statement。
metadata
class SpringJdbcKronosWrapper(
private val dataSource: DataSource
) : KronosDataSourceWrapper {
override val url: String
override val userName: String
override val dbType: DBType
private val namedJdbc = NamedParameterJdbcTemplate(dataSource)
private val transactionManager = DataSourceTransactionManager(dataSource)
init {
dataSource.connection.use { conn ->
url = conn.metaData.url
userName = conn.metaData.userName ?: ""
dbType = DBType.fromName(conn.metaData.databaseProductName)
}
}
}委托查询
KAtomicQueryTask.sql使用命名参数,paramMap保存参数值,targetType保存完整的目标KType。自定义wrapper只需为Map、标量、KPojo和DTO实现一个列表方法和一个单行方法。
query mapping
import kotlin.reflect.jvm.jvmErasure
private fun rowMapper(targetType: KType): RowMapper<Any?> {
val targetClass = targetType.jvmErasure
return when {
targetClass == Map::class -> ColumnMapRowMapper()
targetClass in setOf(String::class, Int::class, Long::class, Boolean::class) ->
SingleColumnRowMapper(targetClass.java)
else -> DataClassRowMapper(targetClass.java)
}
}
override fun toList(task: KAtomicQueryTask): List<Any?> =
namedJdbc.query(task.sql, task.paramMap, rowMapper(task.targetType))
override fun first(task: KAtomicQueryTask): Any? =
toList(task).firstOrNull()query usage
val users = wrapper.toList(
KronosAtomicQueryTask(
sql = "SELECT id, name FROM user WHERE status = :status",
paramMap = mapOf("status" to "ACTIVE"),
targetType = typeOf<User>()
)
)委托写入
KAtomicActionTask携带一组参数。KronosAtomicBatchTask携带一条SQL和多组参数。
writes
override fun update(task: KAtomicActionTask): Int {
return namedJdbc.update(task.sql, task.paramMap)
}
override fun batchUpdate(task: KronosAtomicBatchTask): IntArray {
return namedJdbc.batchUpdate(task.sql, task.paramMapArr ?: emptyArray())
}write usage
val affectedRows = wrapper.batchUpdate(
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)
)
)
)委托事务
transact(...)接收Kronos事务选项,并用TransactionScope执行block。传入Spring管理的JDBC connection后,可以使用TransactionScope savepoint。
transaction
override fun transact(
isolation: TransactionIsolation?,
timeout: Int?,
block: TransactionScope.() -> Any?
): Any? {
val definition = DefaultTransactionDefinition().apply {
isolation?.let { isolationLevel = it.level }
timeout?.let { this.timeout = it }
}
return TransactionTemplate(transactionManager, definition).execute {
val connection = DataSourceUtils.getConnection(dataSource)
try {
TransactionScope(connection).block()
} finally {
DataSourceUtils.releaseConnection(connection, dataSource)
}
}
}transaction usage
wrapper.transact(timeout = 30) {
val point = savepoint("before_status_update")
try {
wrapper.update(
KronosAtomicActionTask(
sql = "UPDATE user SET status = :status WHERE id = :id",
paramMap = mapOf("status" to "ACTIVE", "id" to 1)
)
)
releaseSavepoint(point)
} catch (e: Exception) {
rollbackToSavepoint(point)
throw e
}
}注册wrapper
应用启动时注册wrapper,或把它声明为Spring bean,并在初始化Kronos的位置赋值。
register
@Bean
fun kronosWrapper(dataSource: DataSource): KronosDataSourceWrapper {
return SpringJdbcKronosWrapper(dataSource)
}
@Bean
fun kronosConfiguration(wrapper: KronosDataSourceWrapper): Any {
Kronos.dataSource = { wrapper }
return Any()
}使用顺序参数框架
执行JDBC ?参数的框架可以使用task.parsed()和KronosAtomicBatchTask.parsedArr()。
single task
val task = KronosAtomicActionTask(
sql = "UPDATE user SET status = :status WHERE id = :id",
paramMap = mapOf("status" to "ACTIVE", "id" to 1)
)
val (jdbcSql, params) = task.parsed()
println(jdbcSql)
println(params.toList())output
UPDATE user SET status = ? WHERE id = ?
[ACTIVE, 1]batch task
val (jdbcSql, paramList) = batchTask.parsedArr()
paramList.forEach { params ->
preparedStatement.setObject(1, params[0])
preparedStatement.setObject(2, params[1])
preparedStatement.addBatch()
}内置JDBC实现可以作为顺序参数执行的完整参考:KronosJdbcWrapper.kt。