主要修改点:
- 给所有生成的代码加上了文档注释
- 使用 facebook ktfmt 格式化代码,kotlinpoet 默认生成的代码有时候格式并不符合通用规范,有点丑
- 重构
add
& update
函数,增加 isDynamic
参数,支持插入/更新全量字段或仅非空字段
- 删除
addAll
& updateAll
函数,后续在另外的代码仓库中提供,同时删除了其对应的 ext 和 example 模块
- 删除
checkIfSequenceModified
函数,改为在 add
& update
函数中内联生成其函数体,在 api 包里面添加任何 public 成员都要慎重,尽量坚持最小化原则
- 修改 copyright 年份
- 其他代码格式的微调
修改后,interface 实体类生成的代码如下:
// Auto-generated by ktorm-ksp-compiler, DO NOT EDIT!
package org.ktorm.ksp.example
import java.time.LocalDate
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.Suppress
import kotlin.UInt
import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.EntitySequence
import org.ktorm.entity.sequenceOf
import org.ktorm.ksp.api.Undefined
import org.ktorm.schema.Column
import org.ktorm.schema.Table
import org.ktorm.schema.date
import org.ktorm.schema.int
import org.ktorm.schema.long
import org.ktorm.schema.varchar
/**
* Table employee. This is the kdoc for Employee.
*
* This is the second line.
*/
public open class Employees(alias: String?) : Table<Employee>("employee", alias) {
/**
* Column id. This is the kdoc for id.
*
* This is the second line.
*/
public val id: Column<Int> = int("id").primaryKey().bindTo { it.id }
/** Column name. */
public val name: Column<String> = varchar("name").bindTo { it.name }
/** Column job. */
public val job: Column<String> = varchar("job").bindTo { it.job }
/** Column manager_id. */
public val managerId: Column<Int> = int("manager_id").bindTo { it.managerId }
/** Column hire_date. */
public val hireDate: Column<LocalDate> = date("hire_date").bindTo { it.hireDate }
/** Column salary. */
public val salary: Column<Long> = long("salary").bindTo { it.salary }
/** Column age. */
public val age: Column<UInt> = registerColumn("age", UIntSqlType).bindTo { it.age }
/** Column gender. */
public val gender: Column<Gender> =
registerColumn("gender", IntEnumSqlTypeFactory.createSqlType(Employee::gender)).bindTo { it.gender }
/** Column department_id. */
public val department: Column<Int> = int("department_id").references(Departments) { it.department }
/**
* Return a new-created table object with all properties (including the table name and columns and so on) being
* copied from this table, but applying a new alias given by the parameter.
*/
public override fun aliased(alias: String): Employees = Employees(alias)
/** The default table object of employee. */
public companion object : Employees(alias = null)
}
/** Return the default entity sequence of [Employees]. */
public val Database.employees: EntitySequence<Employee, Employees>
get() = this.sequenceOf(Employees)
/**
* Create an entity of [Employee] and specify the initial values for each properties, properties that doesn't have an
* initial value will left unassigned.
*/
@Suppress("FunctionName")
public fun Employee(
id: Int? = Undefined.of(),
name: String? = Undefined.of(),
job: String? = Undefined.of(),
managerId: Int? = Undefined.of(),
hireDate: LocalDate? = Undefined.of(),
salary: Long? = Undefined.of(),
age: UInt? = Undefined.of(),
gender: Gender? = Undefined.of(),
department: Department? = Undefined.of()
): Employee {
val entity = Entity.create<Employee>()
if (id !== Undefined.of<Int>()) {
entity.id = id ?: error("`id` should not be null.")
}
if (name !== Undefined.of<String>()) {
entity.name = name ?: error("`name` should not be null.")
}
if (job !== Undefined.of<String>()) {
entity.job = job ?: error("`job` should not be null.")
}
if (managerId !== Undefined.of<Int>()) {
entity.managerId = managerId
}
if (hireDate !== Undefined.of<LocalDate>()) {
entity.hireDate = hireDate ?: error("`hireDate` should not be null.")
}
if (salary !== Undefined.of<Long>()) {
entity.salary = salary ?: error("`salary` should not be null.")
}
if ((age as Any?) !== (Undefined.of<UInt>() as Any?)) {
entity.age = age ?: error("`age` should not be null.")
}
if (gender !== Undefined.of<Gender>()) {
entity.gender = gender
}
if (department !== Undefined.of<Department>()) {
entity.department = department ?: error("`department` should not be null.")
}
return entity
}
/**
* Return a deep copy of this entity (which has the same property values and tracked statuses), and alter the specified
* property values.
*/
public fun Employee.copy(
id: Int? = Undefined.of(),
name: String? = Undefined.of(),
job: String? = Undefined.of(),
managerId: Int? = Undefined.of(),
hireDate: LocalDate? = Undefined.of(),
salary: Long? = Undefined.of(),
age: UInt? = Undefined.of(),
gender: Gender? = Undefined.of(),
department: Department? = Undefined.of()
): Employee {
val entity = this.copy()
if (id !== Undefined.of<Int>()) {
entity.id = id ?: error("`id` should not be null.")
}
if (name !== Undefined.of<String>()) {
entity.name = name ?: error("`name` should not be null.")
}
if (job !== Undefined.of<String>()) {
entity.job = job ?: error("`job` should not be null.")
}
if (managerId !== Undefined.of<Int>()) {
entity.managerId = managerId
}
if (hireDate !== Undefined.of<LocalDate>()) {
entity.hireDate = hireDate ?: error("`hireDate` should not be null.")
}
if (salary !== Undefined.of<Long>()) {
entity.salary = salary ?: error("`salary` should not be null.")
}
if ((age as Any?) !== (Undefined.of<UInt>() as Any?)) {
entity.age = age ?: error("`age` should not be null.")
}
if (gender !== Undefined.of<Gender>()) {
entity.gender = gender
}
if (department !== Undefined.of<Department>()) {
entity.department = department ?: error("`department` should not be null.")
}
return entity
}
/** Return the value of [Employee.id]. */
public operator fun Employee.component1(): Int = this.id
/** Return the value of [Employee.name]. */
public operator fun Employee.component2(): String = this.name
/** Return the value of [Employee.job]. */
public operator fun Employee.component3(): String = this.job
/** Return the value of [Employee.managerId]. */
public operator fun Employee.component4(): Int? = this.managerId
/** Return the value of [Employee.hireDate]. */
public operator fun Employee.component5(): LocalDate = this.hireDate
/** Return the value of [Employee.salary]. */
public operator fun Employee.component6(): Long = this.salary
/** Return the value of [Employee.age]. */
public operator fun Employee.component7(): UInt = this.age
/** Return the value of [Employee.gender]. */
public operator fun Employee.component8(): Gender? = this.gender
/** Return the value of [Employee.department]. */
public operator fun Employee.component9(): Department = this.department
data class 实体类生成的代码如下:
// Auto-generated by ktorm-ksp-compiler, DO NOT EDIT!
package org.ktorm.ksp.example
import kotlin.Boolean
import kotlin.Int
import kotlin.String
import kotlin.Suppress
import org.ktorm.database.Database
import org.ktorm.dsl.QueryRowSet
import org.ktorm.dsl.eq
import org.ktorm.entity.EntitySequence
import org.ktorm.entity.sequenceOf
import org.ktorm.expression.ArgumentExpression
import org.ktorm.expression.ColumnAssignmentExpression
import org.ktorm.expression.ColumnExpression
import org.ktorm.expression.InsertExpression
import org.ktorm.expression.UpdateExpression
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import org.ktorm.schema.SqlType
import org.ktorm.schema.int
import org.ktorm.schema.varchar
/** Table student. */
public open class Students(alias: String?) : BaseTable<Student>("student", alias) {
/** Column id. */
public val id: Column<Int> = int("id").primaryKey()
/** Column name. */
public val name: Column<String> = varchar("name")
/** Column age. */
public val age: Column<Int> = int("age")
/**
* Return a new-created table object with all properties (including the table name and columns and so on) being
* copied from this table, but applying a new alias given by the parameter.
*/
public override fun aliased(alias: String): Students = Students(alias)
/** Create an entity object from the specific row of query results. */
public override fun doCreateEntity(row: QueryRowSet, withReferences: Boolean): Student {
return Student(id = row[id], name = row[name], age = row[age]!!)
}
/** The default table object of student. */
public companion object : Students(alias = null)
}
/** Return the default entity sequence of [Students]. */
public val Database.students: EntitySequence<Student, Students>
get() = this.sequenceOf(Students)
/**
* Insert the given entity into this sequence and return the affected record number. If [isDynamic] is set to true, the
* generated SQL will include only the non-null columns.
*
* Note that this function will obtain the generated key from the database and fill it into the corresponding property
* after the insertion completes. But this requires us not to set the primary key’s value beforehand, otherwise, if you
* do that, the given value will be inserted into the database, and no keys generated.
*/
@Suppress("UNCHECKED_CAST")
public fun EntitySequence<Student, Students>.add(entity: Student, isDynamic: Boolean = false): Int {
val isModified =
expression.where != null ||
expression.groupBy.isNotEmpty() ||
expression.having != null ||
expression.isDistinct ||
expression.orderBy.isNotEmpty() ||
expression.offset != null ||
expression.limit != null
if (isModified) {
val msg =
"Entity manipulation functions are not supported by this sequence object. " +
"Please call on the origin sequence returned from database.sequenceOf(table)"
throw UnsupportedOperationException(msg)
}
val assignments = LinkedHashMap<Column<*>, Any?>()
if (isDynamic) {
entity.id?.let { assignments[sourceTable.id] = it }
entity.name?.let { assignments[sourceTable.name] = it }
entity.age.let { assignments[sourceTable.age] = it }
} else {
entity.id?.let { assignments[sourceTable.id] = it }
entity.name.let { assignments[sourceTable.name] = it }
entity.age.let { assignments[sourceTable.age] = it }
}
if (assignments.isEmpty()) {
return 0
}
val expression = // AliasRemover.visit(
InsertExpression(
table = sourceTable.asExpression(),
assignments =
assignments.map { (col, argument) ->
ColumnAssignmentExpression(
column = col.asExpression() as ColumnExpression<Any>,
expression = ArgumentExpression(argument, col.sqlType as SqlType<Any>)
)
}
)
// )
if (entity.id != null) {
return database.executeUpdate(expression)
} else {
val (effects, rowSet) = database.executeUpdateAndRetrieveKeys(expression)
if (rowSet.next()) {
val generatedKey = sourceTable.id.sqlType.getResult(rowSet, 1)
if (generatedKey != null) {
if (database.logger.isDebugEnabled()) {
database.logger.debug("Generated Key: $generatedKey")
}
entity.id = generatedKey
}
}
return effects
}
}
/**
* Update the given entity to the database and return the affected record number. If [isDynamic] is set to true, the
* generated SQL will include only the non-null columns.
*/
@Suppress("UNCHECKED_CAST")
public fun EntitySequence<Student, Students>.update(entity: Student, isDynamic: Boolean = false): Int {
val isModified =
expression.where != null ||
expression.groupBy.isNotEmpty() ||
expression.having != null ||
expression.isDistinct ||
expression.orderBy.isNotEmpty() ||
expression.offset != null ||
expression.limit != null
if (isModified) {
val msg =
"Entity manipulation functions are not supported by this sequence object. " +
"Please call on the origin sequence returned from database.sequenceOf(table)"
throw UnsupportedOperationException(msg)
}
val assignments = LinkedHashMap<Column<*>, Any?>()
if (isDynamic) {
entity.name?.let { assignments[sourceTable.name] = it }
entity.age.let { assignments[sourceTable.age] = it }
} else {
entity.name.let { assignments[sourceTable.name] = it }
entity.age.let { assignments[sourceTable.age] = it }
}
if (assignments.isEmpty()) {
return 0
}
val conditions = sourceTable.id eq entity.id!!
val expression = // AliasRemover.visit(
UpdateExpression(
table = sourceTable.asExpression(),
assignments =
assignments.map { (col, argument) ->
ColumnAssignmentExpression(
column = col.asExpression() as ColumnExpression<Any>,
expression = ArgumentExpression(argument, col.sqlType as SqlType<Any>)
)
},
where = conditions
)
// )
return database.executeUpdate(expression)
}