快速上手

本文将指导您如何快速开始使用Kronos,你可以在这里找到一些示例项目。

准备项目

添加 ORM 运行时、Kronos 编译插件、JDBC wrapper 和本示例使用的数据库驱动。

build.gradle.kts

plugins {
    kotlin("jvm") version "2.4.21"
    id("com.kotlinorm.kronos-gradle-plugin") version "0.4.0"
}

kotlin {
    compilerOptions {
        freeCompilerArgs.addAll("-Xcollection-literals", "-Xcontext-sensitive-resolution")
    }
}

dependencies {
    implementation("com.kotlinorm:kronos-core:0.4.0")
    implementation("com.kotlinorm:kronos-jdbc-wrapper:0.4.0")
    implementation("org.apache.commons:commons-dbcp2:<latest-stable>")
    implementation("com.mysql:mysql-connector-j:<latest-stable>")
}

下面的代码使用名为 kronos 的 MySQL 数据库。将 <latest-stable> 替换为当前 JDK 和数据库服务端可用的驱动、连接池稳定版本。

编译插件是可运行配置的一部分。它会生成 KPojo 元数据、动态成员和查询投影类型。完整配置见 📚编译器插件。

配置 wrapper

在执行 ORM 操作前创建一个 wrapper,并设置到 Kronos.dataSource。本示例把 wrapper 放在文件作用域,DDL 和 CRUD 代码都能复用同一个实例。

Db.kt

import com.kotlinorm.Kronos
import com.kotlinorm.wrappers.KronosJdbcWrapper
import com.kotlinorm.beans.config.KronosCommonStrategy
import com.kotlinorm.beans.dsl.Field
import org.apache.commons.dbcp2.BasicDataSource

val wrapper by lazy {
    KronosJdbcWrapper(
        BasicDataSource().apply {
            driverClassName = "com.mysql.cj.jdbc.Driver"
            url = "jdbc:mysql://localhost:3306/kronos?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC"
            username = "user"
            password = "******"
        }
    )
}

fun configureKronos() {
    with(Kronos) {
        dataSource = { wrapper }
        tableNamingStrategy = lineHumpNamingStrategy
        fieldNamingStrategy = lineHumpNamingStrategy
        defaultDateFormat = "yyyy-MM-dd HH:mm:ss"
        createTimeStrategy = KronosCommonStrategy(enabled = true, field = Field("create_time", "createTime"))
        updateTimeStrategy = KronosCommonStrategy(enabled = true, field = Field("update_time", "updateTime"))
        logicDeleteStrategy = KronosCommonStrategy(enabled = true, field = Field("deleted"))
        optimisticLockStrategy = KronosCommonStrategy(enabled = true, field = Field("version"))
    }
}

自定义 wrapper 和数据库类型识别见 📚连接到数据库 和 📚自定义 Wrapper。

定义 KPojo 类

创建 Kotlin data class,实现 KPojo,并使用注解声明表名、主键、索引和通用字段。

Director.kt

import com.kotlinorm.annotations.CreateTime
import com.kotlinorm.annotations.Default
import com.kotlinorm.annotations.LogicDelete
import com.kotlinorm.annotations.PrimaryKey
import com.kotlinorm.annotations.UpdateTime
import com.kotlinorm.annotations.Version
import com.kotlinorm.interfaces.KPojo
import java.time.LocalDateTime

data class Director(
    @PrimaryKey(identity = true)
    var id: Int? = null,
    var name: String? = null,
    @CreateTime
    var createTime: LocalDateTime? = null,
    @UpdateTime
    var updateTime: LocalDateTime? = null,
    @LogicDelete
    @Default("0") // @Default("false") for Postgres
    var deleted: Boolean? = false,
    @Version
    var version: Int? = 0
) : KPojo

Movie.kt

import com.kotlinorm.annotations.Cascade
import com.kotlinorm.annotations.Column
import com.kotlinorm.annotations.CreateTime
import com.kotlinorm.annotations.Default
import com.kotlinorm.annotations.LogicDelete
import com.kotlinorm.annotations.PrimaryKey
import com.kotlinorm.annotations.Table
import com.kotlinorm.annotations.TableIndex
import com.kotlinorm.annotations.UpdateTime
import com.kotlinorm.interfaces.KPojo
import java.time.LocalDateTime

@Table("tb_movie")
@TableIndex("idx_movie_director", ["name", "director_id"], "UNIQUE", "BTREE")
data class Movie(
    @PrimaryKey(identity = true)
    var id: Int? = null,
    @Column("name")
    var name: String? = null,
    var directorId: Int? = null,
    @Cascade(["directorId"], ["id"])
    var director: Director? = null,
    @CreateTime
    var createTime: LocalDateTime? = null,
    @UpdateTime
    var updateTime: LocalDateTime? = null,
    @LogicDelete
    @Default("0") // @Default("false") for Postgres
    var deleted: Boolean? = false
) : KPojo

映射规则见 📚KPojo、📚注解 和 📚索引。

运行第一条闭环

调用 configureKronos(),创建表,插入一个导演,再用 query-by-example 查询回来。

Main.kt

import com.kotlinorm.orm.insert.insert
import com.kotlinorm.orm.select.select

fun main() {
    configureKronos()

    wrapper.table.createTable(Director())
    wrapper.table.createTable(Movie())

    val result = Director(name = "Kronos")
        .insert()
        .execute()

    val affectedRows = result.affectedRows

    val director = Director(name = "Kronos")
        .select()
        .where()
        .first()
}

在 MySQL 下,插入和查询会生成类似 SQL。

MySQL

INSERT INTO `director` (`name`, `create_time`, `update_time`, `deleted`, `version`)
VALUES (:name, :createTime, :updateTime, :deleted, :version)

SELECT `id`, `name`, `create_time` AS `createTime`, `update_time` AS `updateTime`, `deleted`, `version`
FROM `director`
WHERE `director`.`name` = :name
  AND `deleted` = 0

DDL 用法见 📚表操作,写入用法见 📚插入,where() 行为见 📚条件 DSL。构建或运行结果与本流程不一致时,见 📚故障排查。