update ts-database

This commit is contained in:
tursom 2021-11-17 14:40:33 +08:00
parent 8bddb93f4b
commit 49a6df2e55
63 changed files with 1148 additions and 249 deletions

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.5.21"
kotlin("jvm") version "1.5.31"
`maven-publish`
id("ts-gradle")
}

View File

@ -25,6 +25,8 @@ include("ts-web")
include("ts-web:ts-web-netty")
include("ts-web:ts-web-coroutine")
include("ts-database")
include("ts-database:ts-ktorm")
include("ts-database:ts-mybatisplus")
include("ts-database:ts-mongodb")
include("ts-database:ts-mongodb:ts-mongodb-spring")
include("ts-database:ts-redis")

View File

@ -629,3 +629,17 @@ inline operator fun <reified T> Array<out T>.plus(other: Array<out T>): Array<T>
System.arraycopy(other, 0, array, size, other.size)
return array.uncheckedCast()
}
val <T : Any> KClass<T>.allMemberPropertiesSequence: Sequence<KProperty1<T, *>>
get() = sequence {
yieldAll(memberProperties)
var superClass = superclasses.firstOrNull {
!it.java.isInterface
}
while (superClass != null) {
yieldAll(superClass.memberProperties.uncheckedCast<Collection<KProperty1<T, *>>>())
superClass = superClass.superclasses.firstOrNull {
!it.java.isInterface
}
}
}

View File

@ -0,0 +1,22 @@
package cn.tursom.core.reference
import java.io.Closeable
/**
* 通过垃圾回收机制实现的自动关闭
* 记得保存StrongReference对象防止Closeable意外关闭
*/
class AutoCloseFreeReference(
private val closeable: Closeable,
r: StrongReference<Closeable>,
) : FreeReference<StrongReference<Closeable>>(r) {
override fun release() {
closeable.close()
}
}
fun <T : Closeable> T.registerAutoClose(): StrongReference<T> {
val r = StrongReference(this)
AutoCloseFreeReference(this, r)
return r
}

View File

@ -0,0 +1,5 @@
package cn.tursom.core.reference
open class StrongReference<out T>(
val r: T,
)

View File

@ -34,7 +34,7 @@ object Parser {
clazz.isInstance(yaml) -> yaml.cast()
clazz.isInheritanceFrom(Enum::class.java) -> try {
val valueOf = clazz.getDeclaredMethod("valueOf", String::class.java)
valueOf.invoke(null, yaml.toString().toUpperCase()).cast<T?>()
valueOf.invoke(null, yaml.toString().uppercase(Locale.getDefault())).cast<T?>()
} catch (e: Exception) {
null
}

View File

@ -6,6 +6,7 @@ import cn.tursom.core.isStatic
import cn.tursom.core.uncheckedCast
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.util.*
inline fun <reified T : Annotation> Class<*>.getAnnotation(): T? = getAnnotation(T::class.java)
inline fun <reified T : Annotation> Field.getAnnotation(): T? = getAnnotation(T::class.java)
@ -40,7 +41,7 @@ fun <T : Enum<out T>> Class<out T>.valueOf(value: String): T? {
valueOf.invoke(null, value) as T
} catch (e: Exception) {
try {
valueOf?.invoke(null, value.toUpperCase()) as? T?
valueOf?.invoke(null, value.uppercase(Locale.getDefault())) as? T?
} catch (e: Exception) {
null
}

View File

@ -0,0 +1,10 @@
package cn.tursom.core.coroutine
import kotlinx.coroutines.CoroutineScope
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
object GlobalScope : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}

View File

@ -0,0 +1,22 @@
plugins {
kotlin("jvm")
`maven-publish`
id("ts-gradle")
}
dependencies {
api(kotlin("stdlib-jdk8"))
api(kotlin("reflect"))
implementation(project(":ts-core"))
implementation(project(":ts-core:ts-clone"))
implementation(project(":ts-core:ts-log"))
api(group = "org.ktorm", name = "ktorm-core", version = "3.4.1")
compileOnly(group = "com.google.code.gson", name = "gson", version = "2.8.7")
testApi(group = "junit", name = "junit", version = "4.13.2")
}

View File

@ -1,10 +1,10 @@
package cn.tursom.database
package cn.tursom.database.ktorm
import cn.tursom.core.clone.Property
import cn.tursom.core.clone.inject
import cn.tursom.core.clone.instance
import cn.tursom.core.uncheckedCast
import com.baomidou.mybatisplus.annotation.TableField
import cn.tursom.database.ktorm.annotations.KtormTableField
import org.ktorm.dsl.QueryRowSet
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
@ -14,6 +14,7 @@ import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
open class AutoTable<T : Any>(
@ -24,16 +25,18 @@ open class AutoTable<T : Any>(
schema: String? = null,
val unsafe: Boolean = true,
) : BaseTable<T>(tableName, alias, catalog, schema, entityClass) {
private val fieldMap: Map<String, KProperty<*>>
private val fieldColumns: MutableMap<KProperty<*>, Column<*>> = HashMap()
private val fieldMap: Map<String, KProperty1<T, *>>
private val fieldColumns: MutableMap<KProperty1<T, *>, Column<*>> = HashMap()
private val fieldNameColumnMap: MutableMap<String, Column<*>> = HashMap()
val fieldColumnsMap: Map<KProperty1<T, *>, Column<*>> by ::fieldColumns
init {
fieldMap = entityClass.memberProperties.associateBy { it.simpTableField }
entityClass.memberProperties.forEach {
val field = it.javaField ?: return@forEach
val tableField: TableField? = field.getAnnotation(TableField::class.java)
if (tableField?.exist == false) return@forEach
it.isAccessible = true
if (field.getAnnotation(KtormTableField::class.java)?.exist == false) return@forEach
//TypeAdapterFactory.register(this, it)
val column = TypeAdapterFactory.register(this, it) ?: return@forEach
fieldColumns[it] = column
@ -50,7 +53,8 @@ open class AutoTable<T : Any>(
return instance
}
operator fun <R : Any> get(property: KProperty1<in T, R?>): Column<R> = fieldColumns[property].uncheckedCast()
operator fun <R : Any> get(property: KProperty1<in T, R?>): Column<R> =
fieldColumns[property.uncheckedCast()].uncheckedCast()
//operator fun <R : Any> get(property: KProperty1<T, R?>): Column<R> = this[property.simpTableField].cast()
fun <V : Any> field(): FieldProxy<T, V> = fieldProxyInstance.uncheckedCast()

View File

@ -1,4 +1,4 @@
package cn.tursom.database
package cn.tursom.database.ktorm
import cn.tursom.core.Utils
import com.google.gson.Gson

View File

@ -0,0 +1,63 @@
package cn.tursom.database.ktorm
import cn.tursom.core.uncheckedCast
import org.ktorm.database.Database
import org.ktorm.dsl.*
import org.ktorm.schema.Column
import org.ktorm.schema.ColumnDeclaring
inline fun <reified T : Any> Database.update(
noinline block: UpdateStatementBuilder.(AutoTable<T>) -> Unit
): Int {
return update(AutoTable[T::class], block)
}
inline fun <reified T : Any> Database.batchUpdate(
noinline block: BatchUpdateStatementBuilder<AutoTable<T>>.() -> Unit
): IntArray {
return batchUpdate(AutoTable[T::class], block)
}
inline fun <reified T : Any> Database.insert(
noinline block: AssignmentsBuilder.(AutoTable<T>) -> Unit
): Int {
return insert(AutoTable[T::class], block)
}
inline fun <reified T : Any> Database.insert(
value: T
): Int {
val table = AutoTable[T::class]
return insert(table) {
table.fieldColumnsMap.forEach { (property, column) ->
val columnValue = property.get(value) ?: return@forEach
set(column.uncheckedCast(), columnValue)
}
}
}
inline fun <reified T : Any> Database.insertAndGenerateKey(
noinline block: AssignmentsBuilder.(AutoTable<T>) -> Unit
): Any {
return insertAndGenerateKey(AutoTable[T::class], block)
}
inline fun <reified T : Any> Database.batchInsert(
noinline block: BatchInsertStatementBuilder<AutoTable<T>>.() -> Unit
): IntArray {
return batchInsert(AutoTable[T::class], block)
}
inline fun <reified T : Any> Query.insertTo(vararg columns: Column<*>): Int {
return insertTo(AutoTable[T::class], columns = columns)
}
inline fun <reified T : Any> Database.delete(
noinline predicate: (AutoTable<T>) -> ColumnDeclaring<Boolean>
): Int {
return delete(AutoTable[T::class], predicate)
}
inline fun <reified T : Any> Database.deleteAll(): Int {
return deleteAll(AutoTable[T::class])
}

View File

@ -1,6 +1,6 @@
@file:Suppress("unused")
package cn.tursom.database
package cn.tursom.database.ktorm
import org.ktorm.dsl.*
import org.ktorm.expression.*

View File

@ -1,6 +1,6 @@
@file:Suppress("unused")
package cn.tursom.database
package cn.tursom.database.ktorm
import cn.tursom.core.Utils
import cn.tursom.core.uncheckedCast

View File

@ -8,11 +8,10 @@
@file:Suppress("unused")
package cn.tursom.database
package cn.tursom.database.ktorm
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableName
import org.apache.ibatis.type.TypeHandler
import cn.tursom.database.ktorm.annotations.KtormTableField
import cn.tursom.database.ktorm.annotations.KtormTableName
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import kotlin.reflect.KClass
@ -36,46 +35,44 @@ interface TableField<T> {
val properties: Array<out KProperty1<out T, *>>
val allField: Array<out String>
val fullNameField: Array<out String>
val typeHandlerMap: Map<KProperty1<out T, *>, TypeHandler<Any>>
//operator fun get(field: KProperty<*>): String = fieldMap[field] ?: field.tableField
operator fun get(field: KProperty1<out T, *>): String = fieldMap[field] ?: field.simpTableField
}
val <T> Iterable<KProperty1<out T, *>>.filterNotExists
get() = filter {
it.javaField != null &&
it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java) == null &&
!Modifier.isTransient(it.javaField?.modifiers ?: Modifier.TRANSIENT) &&
it.javaField?.getAnnotation(com.baomidou.mybatisplus.annotation.TableField::class.java)?.exist != false
}
@get:JvmName("filterNotExistsPair")
val <T> Iterable<Pair<KProperty1<T, *>, *>>.filterNotExists
get() = filter { (it, _) ->
it.javaField != null &&
it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java) == null &&
!Modifier.isTransient(it.javaField?.modifiers ?: Modifier.TRANSIENT) &&
it.javaField?.getAnnotation(com.baomidou.mybatisplus.annotation.TableField::class.java)?.exist != false
}
@get:JvmName("filterNotExistsKProperty")
val Iterable<KProperty<*>>.filterNotExists
val <T> Iterable<KProperty1<out T, *>>.filterNotExists: List<KProperty1<out T, *>>
get() = filter {
it.javaField != null &&
(it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java)) == null &&
!Modifier.isTransient(it.javaField?.modifiers ?: Modifier.TRANSIENT) &&
it.javaField?.getAnnotation(com.baomidou.mybatisplus.annotation.TableField::class.java)?.exist != false
it.javaField?.getAnnotation(KtormTableField::class.java)?.exist != false
}
@get:JvmName("filterNotExistsPair")
val <T> Iterable<Pair<KProperty1<T, *>, *>>.filterNotExists: List<Pair<KProperty1<T, *>, *>>
get() = filter { (it, _) ->
it.javaField != null &&
(it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java)) == null &&
!Modifier.isTransient(it.javaField?.modifiers ?: Modifier.TRANSIENT) &&
it.javaField?.getAnnotation(KtormTableField::class.java)?.exist != false
}
@get:JvmName("filterNotExistsKProperty")
val Iterable<KProperty<*>>.filterNotExists: List<KProperty<*>>
get() = filter {
it.javaField != null &&
(it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java)) == null &&
!Modifier.isTransient(it.javaField?.modifiers ?: Modifier.TRANSIENT) &&
it.javaField?.getAnnotation(KtormTableField::class.java)?.exist != false
}
@get:JvmName("filterNotExistsKPropertyPair")
val Iterable<Pair<KProperty<*>, *>>.filterNotExists
val Iterable<Pair<KProperty<*>, *>>.filterNotExists: List<Pair<KProperty<*>, *>>
get() = filter { (it, _) ->
it.javaField != null &&
it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java) == null &&
(it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java)) == null &&
!Modifier.isTransient(it.javaField?.modifiers ?: Modifier.TRANSIENT) &&
it.javaField?.getAnnotation(com.baomidou.mybatisplus.annotation.TableField::class.java)?.exist != false
it.javaField?.getAnnotation(KtormTableField::class.java)?.exist != false
}
val String.sqlName: String
@ -94,9 +91,9 @@ val String.sqlName: String
return sb.toString()
}
val KClass<*>.tableName: String get() = findAnnotation<TableName>()?.value ?: simpleName!!.sqlName
val Class<*>.tableName: String get() = getAnnotation(TableName::class.java)?.value ?: simpleName.sqlName
val KProperty<*>.tableFieldName: String? get() = javaField?.getAnnotation(TableField::class.java)?.value
val KClass<*>.tableName: String get() = findAnnotation<KtormTableName>()?.name ?: simpleName!!.sqlName
val Class<*>.tableName: String get() = getAnnotation(KtormTableName::class.java)?.name ?: simpleName.sqlName
val KProperty<*>.tableFieldName: String? get() = javaField?.getAnnotation(KtormTableField::class.java)?.name
val KProperty<*>.simpTableField: String get() = tableFieldName ?: name.sqlName
val KProperty<*>.selectionTableField: String
get() = tableFieldName?.let { if (it.isNotEmpty()) "$it as ${name.sqlName}" else null } ?: name.sqlName
@ -104,9 +101,9 @@ val KProperty<*>.selectionTableField: String
inline val <reified T> KProperty1<out T, *>.tableField: String
get() {
val companion = T::class.companionObjectInstance
return if (companion is cn.tursom.database.TableField<*>) {
return if (companion is TableField<*>) {
@Suppress("UNCHECKED_CAST")
companion as cn.tursom.database.TableField<T>
companion as TableField<T>
companion[this]
} else {
selectionTableField
@ -116,9 +113,9 @@ inline val <reified T> KProperty1<out T, *>.tableField: String
inline val <reified T : Any> KProperty1<out T, *>.fullTableField: String
get() {
val companion = T::class.companionObjectInstance
return if (companion is cn.tursom.database.TableField<*>) {
return if (companion is TableField<*>) {
@Suppress("UNCHECKED_CAST")
companion as cn.tursom.database.TableField<T>
companion as TableField<T>
companion.fullFieldMap[this] ?: "${companion.tableName}.${companion[this]}"
} else {
"${T::class.tableName}.$selectionTableField"
@ -133,8 +130,8 @@ val KProperty<*>.fullTableField: String
val Field.tableField: String
get() {
val tableField = getAnnotation(TableField::class.java)
return tableField?.value?.let { if (it.isNotEmpty()) "$it as ${name.sqlName}" else null } ?: name.sqlName
val tableField = getAnnotation(KtormTableField::class.java)
return tableField?.name?.let { if (it.isNotEmpty()) "$it as ${name.sqlName}" else null } ?: name.sqlName
}
val KProperty<*>.directTableField: String
@ -143,9 +140,9 @@ val KProperty<*>.directTableField: String
inline val <reified T> KProperty1<out T, *>.directTableField: String
get() {
val companion = T::class.companionObjectInstance
return if (companion is cn.tursom.database.TableField<*>) {
return if (companion is TableField<*>) {
@Suppress("UNCHECKED_CAST")
companion as cn.tursom.database.TableField<T>
companion as TableField<T>
companion.simpFieldMap[this] ?: simpTableField
} else {
simpTableField
@ -158,9 +155,9 @@ inline val <reified T> Collection<KProperty1<out T, *>>.directTableField: Array<
val companion = T::class.companionObjectInstance
val fieldList = arrayOfNulls<String>(size)
filterNotExists.forEach {
if (companion is cn.tursom.database.TableField<*>) {
if (companion is TableField<*>) {
@Suppress("UNCHECKED_CAST")
companion as cn.tursom.database.TableField<T>
companion as TableField<T>
companion.simpFieldMap[it] ?: it.simpTableField
} else {
it.simpTableField
@ -172,17 +169,17 @@ inline val <reified T> Collection<KProperty1<out T, *>>.directTableField: Array<
val Field.directTableField: String
get() {
val tableField = getAnnotation(TableField::class.java)
return tableField?.value?.ifEmpty { null } ?: name.sqlName
val tableField = getAnnotation(KtormTableField::class.java)
return tableField?.name?.ifEmpty { null } ?: name.sqlName
}
inline val <reified T> Array<out KProperty1<T, *>>.tableField: Array<out String> get() = asList().tableField
inline val <reified T> Collection<KProperty1<T, *>>.tableField: Array<out String>
get() {
val companion = T::class.companionObjectInstance
return if (companion is cn.tursom.database.TableField<*>) {
return if (companion is TableField<*>) {
@Suppress("UNCHECKED_CAST")
companion as cn.tursom.database.TableField<T>
companion as TableField<T>
filterNotExists.map { companion[it] }
} else {
filterNotExists.map { it.simpTableField }
@ -192,9 +189,9 @@ inline val <reified T> Collection<KProperty1<T, *>>.tableField: Array<out Strin
inline val <reified T> Map<out KProperty1<T, *>, *>.tableField: Map<String, *>
get() {
val companion = T::class.companionObjectInstance
return if (companion is cn.tursom.database.TableField<*>) {
return if (companion is TableField<*>) {
@Suppress("UNCHECKED_CAST")
companion as cn.tursom.database.TableField<T>
companion as TableField<T>
mapKeys { companion.simpFieldMap[it.key] ?: it.key.directTableField }
} else {
mapKeys { it.key.directTableField }
@ -205,9 +202,9 @@ inline val <reified T> Array<out Pair<KProperty1<T, *>, *>>.tableField: Map<Stri
inline val <reified T> Collection<Pair<KProperty1<T, *>, *>>.tableField: Map<String, *>
get() {
val companion = T::class.companionObjectInstance
return if (companion is cn.tursom.database.TableField<*>) {
return if (companion is TableField<*>) {
@Suppress("UNCHECKED_CAST")
companion as cn.tursom.database.TableField<T>
companion as TableField<T>
filterNotExists.associate { (companion.simpFieldMap[it.first] ?: it.first.directTableField) to it.second }
} else {
filterNotExists.associate { it.first.directTableField to it.second }
@ -237,9 +234,9 @@ inline val <reified T> Collection<KProperty1<T, *>>.fullTableField: Array<out S
get() {
val tableName = T::class.tableName
val companion = T::class.companionObjectInstance
return if (companion is cn.tursom.database.TableField<*>) {
return if (companion is TableField<*>) {
@Suppress("UNCHECKED_CAST")
companion as cn.tursom.database.TableField<T>
companion as TableField<T>
filterNotExists.map { "$tableName.${companion[it]}" }
} else {
filterNotExists.map { "$tableName.${it.simpTableField}" }

View File

@ -1,8 +1,8 @@
package cn.tursom.database
package cn.tursom.database.ktorm
import cn.tursom.core.uncheckedCast
import cn.tursom.database.ktorm.annotations.KtormTableField
import cn.tursom.log.impl.Slf4jImpl
import org.apache.ibatis.type.TypeHandler
import java.lang.reflect.Modifier
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
@ -31,7 +31,6 @@ open class TableFieldImpl<T>(clazz: KClass<*>? = null) : TableField<T> {
final override val allField: Array<out String>
final override val fullNameField: Array<out String>
final override val properties: Array<KProperty1<T, *>>
override val typeHandlerMap = HashMap<KProperty1<out T, *>, TypeHandler<Any>>()
init {
if (clazz == null && this.javaClass == TableFieldImpl::class.java) {
@ -50,9 +49,9 @@ open class TableFieldImpl<T>(clazz: KClass<*>? = null) : TableField<T> {
.uncheckedCast<Collection<KProperty1<out T, *>>>()
.filter {
it.javaField != null &&
it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java) == null &&
(it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java)) == null &&
!Modifier.isTransient(it.javaField?.modifiers ?: Modifier.TRANSIENT) &&
it.javaField?.getAnnotation(com.baomidou.mybatisplus.annotation.TableField::class.java)?.exist != false
it.javaField?.getAnnotation(KtormTableField::class.java)?.exist != false
}
.forEach {
val simpTableField = it.simpTableField
@ -60,7 +59,6 @@ open class TableFieldImpl<T>(clazz: KClass<*>? = null) : TableField<T> {
simpFieldMap[it] = simpTableField
fieldMap[it] = it.selectionTableField
fullFieldMap[it] = "$tableName.${it.name.sqlName}"
it.findAnnotation<com.baomidou.mybatisplus.annotation.TableField>()?.typeHandler
}
properties = fieldMap.keys.toTypedArray().uncheckedCast()
allField = fieldMap.values.toTypedArray()

View File

@ -1,4 +1,4 @@
package cn.tursom.database
package cn.tursom.database.ktorm
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column

View File

@ -1,4 +1,4 @@
package cn.tursom.database
package cn.tursom.database.ktorm
import cn.tursom.core.getClassByPackage
import cn.tursom.core.reflect.InstantAllocator

View File

@ -1,4 +1,4 @@
package cn.tursom.database.annotations
package cn.tursom.database.ktorm.annotations
import kotlin.reflect.KClass

View File

@ -1,4 +1,4 @@
package cn.tursom.database.annotations
package cn.tursom.database.ktorm.annotations
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)

View File

@ -0,0 +1,9 @@
package cn.tursom.database.ktorm.annotations
@MustBeDocumented
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD, AnnotationTarget.ANNOTATION_CLASS)
annotation class KtormTableField(
val name: String = "",
val exist: Boolean = true,
)

View File

@ -0,0 +1,8 @@
package cn.tursom.database.ktorm.annotations
@MustBeDocumented
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD, AnnotationTarget.ANNOTATION_CLASS)
annotation class KtormTableName(
val name: String = "",
)

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.boolean
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.boolean
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import kotlin.reflect.KProperty1

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.bytes
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.bytes
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import kotlin.reflect.KProperty1

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.jdbcDate
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.jdbcDate
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.sql.Date

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.double
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.double
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import kotlin.reflect.KProperty1

View File

@ -1,8 +1,8 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.core.uncheckedCast
import cn.tursom.database.TypeAdapter
import cn.tursom.database.simpTableField
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.simpTableField
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import org.ktorm.schema.EnumSqlType

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.float
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.float
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import kotlin.reflect.KProperty1

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.timestamp
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.timestamp
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.time.Instant

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.int
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.int
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import kotlin.reflect.KProperty1

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.decimal
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.decimal
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.math.BigDecimal

View File

@ -1,8 +1,8 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.annotations.Json
import cn.tursom.database.json
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.annotations.Json
import cn.tursom.database.ktorm.json
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import kotlin.reflect.KProperty1

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.date
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.date
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.time.LocalDate

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.datetime
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.datetime
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.time.LocalDateTime

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.time
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.time
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.time.LocalTime

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.long
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.long
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import kotlin.reflect.KProperty1

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.monthDay
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.monthDay
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.time.MonthDay

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.varchar
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.varchar
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import kotlin.reflect.KProperty1

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.jdbcTime
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.jdbcTime
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.sql.Time

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.jdbcTimestamp
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.jdbcTimestamp
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.sql.Timestamp

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.uuid
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.uuid
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.util.*

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.year
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.year
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.time.Year

View File

@ -1,7 +1,7 @@
package cn.tursom.database.typeadapter
package cn.tursom.database.ktorm.typeadapter
import cn.tursom.database.TypeAdapter
import cn.tursom.database.yearMonth
import cn.tursom.database.ktorm.TypeAdapter
import cn.tursom.database.ktorm.yearMonth
import org.ktorm.schema.BaseTable
import org.ktorm.schema.Column
import java.time.YearMonth

View File

@ -0,0 +1,22 @@
plugins {
kotlin("jvm")
`maven-publish`
id("ts-gradle")
}
dependencies {
api(kotlin("stdlib-jdk8"))
api(kotlin("reflect"))
implementation(project(":ts-core"))
implementation(project(":ts-core:ts-clone"))
implementation(project(":ts-core:ts-log"))
implementation(group = "com.baomidou", name = "mybatis-plus", version = "3.4.3.2")
compileOnly(group = "com.google.code.gson", name = "gson", version = "2.8.7")
testApi(group = "junit", name = "junit", version = "4.13.2")
}

View File

@ -0,0 +1,134 @@
package cn.tursom.database.mybatisplus
import cn.tursom.core.uncheckedCast
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper
import com.baomidou.mybatisplus.core.conditions.Wrapper
import com.baomidou.mybatisplus.core.conditions.interfaces.Compare
import java.util.*
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
@Suppress("unused")
@MybatisPlusEnhanceDslMaker
interface CompareEnhance<T, out W : AbstractWrapper<T, String, out W>, Children : Wrapper<T>> :
EnhanceEntityClassEnhance<T>,
RegexAbstractWrapperEnhance<T, W, Children> {
val compare: Compare<Children, String> get() = uncheckedCast()
/**
* QueryWrapper<T>().eq(T::fieldName, value)
*/
infix fun KProperty1<T, *>.eq(
value: Any,
): Children = compare.eq(getFieldData()!!.name, value)
infix fun KProperty<*>.eq(
value: Any,
): Children = compare.eq(getFieldData()!!.name, value)
fun eq(
column: Pair<KProperty1<T, *>, Any?>,
): Children = compare.eq(column.first.getFieldData()!!.name, column.second)
fun eq(
vararg pair: Pair<KProperty1<T, *>, Any?>,
): Children = compare.eq(pair.asSequence())
fun eq(
pair: Collection<Pair<KProperty1<T, *>, Any>>,
): Children = compare.eq(pair.asSequence())
fun eq(
pair: Sequence<Pair<KProperty<*>, *>>,
): Children = compare.allEq(
pair.mapNotNull { (property, value) ->
val fieldData = property.getFieldData() ?: return@mapNotNull null
fieldData.name to value
}.associate {
it
}
)
fun <Children> Compare<Children, String>.eq(vararg pair: Pair<KProperty<*>, *>): Children = eq(pair.asSequence())
fun eq(entity: T): Children {
val eqs = LinkedList<Pair<KProperty1<T, *>, Any>>()
entity!!.javaClass.kotlin.memberProperties.uncheckedCast<Collection<KProperty1<T, *>>>().forEach {
it.isAccessible = true
eqs.add(it to (it(entity) ?: return@forEach))
}
return eq(eqs)
}
fun eqMapEntry(
pair: Sequence<Map.Entry<KProperty<*>, *>>,
): Children = compare.allEq(
pair.mapNotNull { (property, value) ->
val fieldData = property.getFieldData() ?: return@mapNotNull null
fieldData.name to value
}.associate {
it
}
)
/**
* QueryWrapper<T>().allEq(mapOf(
* T::fieldName1 to value1,
* T::fieldName2 to value2,
* ...
* ))
*/
fun allEq(map: Map<out KProperty1<T, *>, *>): Children = compare.eq(map.asSequence())
fun allEq(vararg pair: Pair<KProperty<*>, *>): Children = compare.eq(pair.asSequence())
fun allFullEq(vararg pair: Pair<KProperty<*>, *>): Children = compare.eq(pair.asSequence())
infix fun KProperty1<T, *>.ne(
value: Any?,
): Children = compare.ne(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.gt(
value: Any?,
): Children = compare.gt(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.ge(
value: Any?,
): Children = compare.ge(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.lt(
value: Any?,
): Children = compare.lt(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.le(
value: Any?,
): Children = compare.le(getFieldData()!!.name, value)
fun KProperty1<T, *>.between(
val1: Any?,
val2: Any?,
): Children = compare.between(getFieldData()!!.name, val1, val2)
fun KProperty1<T, *>.notBetween(
val1: Any?,
val2: Any?,
): Children = compare.notBetween(getFieldData()!!.name, val1, val2)
infix fun KProperty1<T, *>.like(
value: Any?,
): Children = compare.like(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.notLike(
value: Any?,
): Children = compare.notLike(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.likeLeft(
value: Any?,
): Children = compare.likeLeft(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.likeRight(
value: Any?,
): Children = compare.likeRight(getFieldData()!!.name, value)
}

View File

@ -0,0 +1,18 @@
package cn.tursom.database.mybatisplus
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
class DdbesQueryWrapper<T>(
override var enhanceEntityClass: Class<T>
) : QueryWrapper<T>(),
QueryEnhance<T, DdbesQueryWrapper<T>>,
DdbesWrapperEnhance<T, QueryWrapper<T>, DdbesQueryWrapper<T>> {
init {
this.entityClass = enhanceEntityClass
}
companion object {
inline operator fun <reified T> invoke() = DdbesQueryWrapper(T::class.java)
}
}

View File

@ -0,0 +1,13 @@
package cn.tursom.database.mybatisplus
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper
class DdbesUpdateWrapper<T>(
override val enhanceEntityClass: Class<T>,
) : UpdateWrapper<T>(),
UpdateEnhance<T, DdbesUpdateWrapper<T>>,
DdbesWrapperEnhance<T, UpdateWrapper<T>, DdbesUpdateWrapper<T>> {
companion object {
inline operator fun <reified T> invoke() = DdbesUpdateWrapper(T::class.java)
}
}

View File

@ -0,0 +1,18 @@
package cn.tursom.database.mybatisplus
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper
import com.baomidou.mybatisplus.core.conditions.Wrapper
import com.baomidou.mybatisplus.core.conditions.interfaces.Compare
import com.baomidou.mybatisplus.core.conditions.interfaces.Func
import com.baomidou.mybatisplus.core.conditions.interfaces.Join
import com.baomidou.mybatisplus.core.conditions.interfaces.Nested
@MybatisPlusEnhanceDslMaker
interface DdbesWrapperEnhance<T, W : AbstractWrapper<T, String, out W>, EnhanceWrapper : Wrapper<T>> :
CompareEnhance<T, W, EnhanceWrapper>,
JoinEnhance<EnhanceWrapper>,
FuncEnhance<T, EnhanceWrapper>,
Compare<W, String>,
Nested<W, W>,
Join<W>,
Func<W, String>

View File

@ -0,0 +1,6 @@
package cn.tursom.database.mybatisplus
@MybatisPlusEnhanceDslMaker
interface EnhanceEntityClassEnhance<T> {
val enhanceEntityClass: Class<T>
}

View File

@ -0,0 +1,48 @@
package cn.tursom.database.mybatisplus
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper
import com.baomidou.mybatisplus.core.conditions.Wrapper
inline fun <C : CompareEnhance<T, W, Children>, T, W : AbstractWrapper<T, String, W>, Children : Wrapper<T>> C.compare(
compare: CompareEnhance<T, W, Children>.() -> Unit
): C {
compare()
return this
}
inline fun <C : FuncEnhance<T, Children>, T, Children : Wrapper<T>> C.func(
func: FuncEnhance<T, Children>.() -> Unit
): C {
func()
return this
}
inline fun <C : JoinEnhance<Children>, Children> C.join(
join: JoinEnhance<Children>.() -> Unit
): C {
join()
return this
}
inline fun <C : QueryEnhance<T, Children>, T, Children : Wrapper<T>> C.query(
query: QueryEnhance<T, Children>.() -> Unit
): C {
query()
return this
}
inline fun <C : UpdateEnhance<T, Children>, T, Children : Wrapper<T>> C.update(
update: UpdateEnhance<T, Children>.() -> Unit
): C {
update()
return this
}
inline fun <C : DdbesWrapperEnhance<T, W, EnhanceWrapper>,
T, W : AbstractWrapper<T, String, out W>, EnhanceWrapper : Wrapper<T>>
C.query(
query: DdbesWrapperEnhance<T, W, EnhanceWrapper>.() -> Unit
): C {
query()
return this
}

View File

@ -0,0 +1,7 @@
package cn.tursom.database.mybatisplus
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class EnhanceField(
val field: String,
)

View File

@ -0,0 +1,67 @@
package cn.tursom.database.mybatisplus
import cn.tursom.core.uncheckedCast
import com.baomidou.mybatisplus.core.conditions.Wrapper
import com.baomidou.mybatisplus.core.conditions.interfaces.Func
import kotlin.reflect.KProperty1
@Suppress("unused")
@MybatisPlusEnhanceDslMaker
interface FuncEnhance<T, out Children : Wrapper<T>> {
val func: Func<out Children, String> get() = uncheckedCast()
fun KProperty1<T, *>.having(
vararg value: Any?,
): Children = func.having(getFieldData()!!.name, value)
fun KProperty1<T, *>.isNull(column: KProperty1<T, *>): Children = func.isNull(column.getFieldData()!!.name)
fun KProperty1<T, *>.isNotNull(column: KProperty1<T, *>): Children = func.isNotNull(column.getFieldData()!!.name)
infix fun KProperty1<T, *>.`in`(
value: Any?,
): Children = func.`in`(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.`in`(
value: Collection<Any?>,
): Children = func.`in`(getFieldData()!!.name, value)
fun KProperty1<T, *>.`in`(
vararg value: Any,
): Children = func.`in`(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.notIn(
value: Collection<Any?>,
): Children = func.notIn(getFieldData()!!.name, value)
fun KProperty1<T, *>.notIn(
vararg value: Any,
): Children = func.notIn(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.inSql(
value: String?,
): Children = func.inSql(getFieldData()!!.name, value)
infix fun KProperty1<T, *>.notInSql(
value: String?,
): Children = func.notInSql(getFieldData()!!.name, value)
fun groupBy(column: KProperty1<T, *>): Children = func.groupBy(column.getFieldData()!!.name)
@Suppress("UNCHECKED_CAST")
fun groupBy(vararg columns: KProperty1<T, *>): Children =
func.groupBy(columns.map { column -> column.getFieldData()!!.name })
fun orderByAsc(column: KProperty1<T, *>): Children = func.orderByAsc(column.getFieldData()!!.name)
@Suppress("UNCHECKED_CAST")
fun orderByAsc(vararg columns: KProperty1<T, *>): Children =
func.orderByAsc(columns.map { column -> column.getFieldData()!!.name })
fun orderByDesc(column: KProperty1<T, *>): Children = func.orderByDesc(column.getFieldData()!!.name).uncheckedCast()
@Suppress("UNCHECKED_CAST")
fun orderByDesc(vararg columns: KProperty1<T, *>): Children =
func.orderByDesc(columns.map { column -> column.getFieldData()!!.name }).uncheckedCast()
}

View File

@ -0,0 +1,75 @@
package cn.tursom.database.mybatisplus
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper
import com.baomidou.mybatisplus.core.metadata.IPage
import com.baomidou.mybatisplus.extension.service.IService
inline fun <reified T> IService<T>.update(
wrapperBuilder: DdbesUpdateWrapper<T>.() -> Unit
): Boolean {
val wrapper = DdbesUpdateWrapper<T>()
wrapper.wrapperBuilder()
return update(wrapper)
}
inline fun <reified T> IService<T>.update(
queryBuilder: DdbesWrapperEnhance<T, UpdateWrapper<T>, DdbesUpdateWrapper<T>>.() -> Unit,
updateBuilder: UpdateEnhance<T, DdbesUpdateWrapper<T>>.() -> Unit
): Boolean {
val wrapper = DdbesUpdateWrapper<T>()
wrapper.queryBuilder()
wrapper.updateBuilder()
return update(wrapper)
}
inline fun <reified T> IService<T>.remove(
wrapperBuilder: DdbesQueryWrapper<T>.() -> Unit
): Boolean {
val wrapper = DdbesQueryWrapper<T>()
wrapper.wrapperBuilder()
return remove(wrapper)
}
inline fun <reified T> IService<T>.getOne(
throwEx: Boolean = true,
wrapperBuilder: DdbesQueryWrapper<T>.() -> Unit
): T {
val wrapper = DdbesQueryWrapper<T>()
wrapper.wrapperBuilder()
return getOne(wrapper, throwEx)
}
inline fun <reified T> IService<T>.count(
wrapperBuilder: DdbesQueryWrapper<T>.() -> Unit
): Long {
val wrapper = DdbesQueryWrapper<T>()
wrapper.wrapperBuilder()
return count(wrapper)
}
inline fun <reified T> IService<T>.list(
wrapperBuilder: DdbesQueryWrapper<T>.() -> Unit
): List<T> {
val wrapper = DdbesQueryWrapper<T>()
wrapper.wrapperBuilder()
return list(wrapper)
}
inline fun <reified T, E : IPage<T>> IService<T>.page(
page: E,
wrapperBuilder: DdbesQueryWrapper<T>.() -> Unit
): E {
val wrapper = DdbesQueryWrapper<T>()
wrapper.wrapperBuilder()
return page(page, wrapper)
}
inline fun <reified T, E : IPage<Map<String, Any>>> IService<T>.pageMaps(
page: E,
wrapperBuilder: DdbesQueryWrapper<T>.() -> Unit
): E {
val wrapper = DdbesQueryWrapper<T>()
wrapper.wrapperBuilder()
return pageMaps(page, wrapper)
}

View File

@ -0,0 +1,21 @@
package cn.tursom.database.mybatisplus
import cn.tursom.core.uncheckedCast
import com.baomidou.mybatisplus.core.conditions.interfaces.Join
@MybatisPlusEnhanceDslMaker
interface JoinEnhance<out Children> {
val join: Join<out Children> get() = uncheckedCast()
/**
* QueryWrapper<T>()
* .xx()
* .xxx()
* ...
* .limit1()
*/
fun limit1(): Children = join.last("LIMIT 1").uncheckedCast()
fun limit(count: Int): Children = join.last("LIMIT $count").uncheckedCast()
fun limit(start: Int, count: Int): Children = join.last("LIMIT $start, $count").uncheckedCast()
}

View File

@ -0,0 +1,5 @@
package cn.tursom.database.mybatisplus
@DslMarker
@Retention(AnnotationRetention.BINARY)
annotation class MybatisPlusEnhanceDslMaker

View File

@ -0,0 +1,46 @@
package cn.tursom.database.mybatisplus
import cn.tursom.core.uncheckedCast
import com.baomidou.mybatisplus.core.conditions.Wrapper
import com.baomidou.mybatisplus.core.conditions.query.Query
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
@Suppress("unused")
@MybatisPlusEnhanceDslMaker
interface QueryEnhance<T, out Children : Wrapper<T>> : EnhanceEntityClassEnhance<T> {
val query: Query<out Children, T, out Any> get() = uncheckedCast()
fun select(
columns: Collection<String>,
): Children = query.select(enhanceEntityClass) {
it.column in columns
}
fun select(
columns: Sequence<String>,
): Children = select(columns.toSet())
/**
* QueryWrapper<T>().select(T::fieldName, value)
*/
fun select(
vararg columns: KProperty1<T, *>,
): Children = select(columns = columns as Array<out KProperty<*>>)
fun select(
vararg columns: KProperty<*>,
): Children = select(columns.mapNotNull {
it.getFieldData()?.field?.name
}.toSet())
fun fullSelect(
vararg columns: KProperty<*>,
): Children = selectMethod(uncheckedCast(), columns.mapNotNull {
it.getFieldData()?.selectionName
}.toTypedArray()).uncheckedCast()
fun joinSelect(
vararg columns: KProperty<*>,
): Children = fullSelect(columns = columns)
}

View File

@ -0,0 +1,22 @@
package cn.tursom.database.mybatisplus
import cn.tursom.core.uncheckedCast
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper
import kotlin.reflect.KProperty1
@MybatisPlusEnhanceDslMaker
interface RegexAbstractWrapperEnhance<T, out W : AbstractWrapper<T, String, out W>, Children> {
val wrapper: W get() = uncheckedCast()
infix fun String.regex(
value: Any
): Children = WrapperEnhance.regex(wrapper, this, value).uncheckedCast()
infix fun KProperty1<T, *>.regex(
value: Any,
): Children = WrapperEnhance.regex(wrapper, getFieldData()!!.name, value).uncheckedCast()
infix fun KProperty1<T, *>.regex(
regex: Regex,
): Children = WrapperEnhance.regex(wrapper, getFieldData()!!.name, regex.toString()).uncheckedCast()
}

View File

@ -0,0 +1,151 @@
/**
* SQL 访问增强工具实现从属性到数据库字段的自动映射
* @author 王景阔
*
* Files::name.tableField
* 可获得 Files name 属性对应的字段名
*/
@file:Suppress("unused")
package cn.tursom.database.mybatisplus
import cn.tursom.core.*
import cn.tursom.core.reference.StrongReference
import cn.tursom.core.reflect.getAnnotation
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.annotation.TableName
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper
import java.lang.reflect.Field
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KProperty
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.jvm.javaField
data class FieldData(
val field: Field,
val name: String,
val selectionName: String,
val tableField: TableField?,
val column: String,
val exist: Boolean = !field.transient && !field.static && tableField?.exist ?: true,
)
val String.sqlName: String
get() {
val sb = StringBuilder()
val iterator = iterator()
sb.append(iterator.nextChar().lowercaseChar())
iterator.forEach {
if (it.isUpperCase()) {
sb.append('_')
sb.append(it.lowercaseChar())
} else {
sb.append(it)
}
}
return sb.toString()
}
val Class<*>.tableName
get() = getAnnotation<TableName>()?.value ?: simpleName.sqlName
internal val allSelectionFieldMap = ConcurrentHashMap<Class<*>, Array<out String>>()
internal val fieldDataMap = ConcurrentHashMap<Field, FieldData>()
internal val kPropertyFieldDataMap = ConcurrentHashMap<KProperty<*>, StrongReference<FieldData?>>()
val Iterable<Field>.filterNotExists
get() = asSequence().filter {
it.getFieldData().exist
}
@get:JvmName("filterNotExistsField")
val Sequence<Field>.filterNotExists
get() = filter {
it.getFieldData().exist
}
@get:JvmName("filterNotExistsKProperty")
val <T : KProperty<*>> Sequence<T>.filterNotExists
get() = filter {
it.getFieldData()?.exist ?: false
}
fun Field.getFieldData(): FieldData = fieldDataMap.getOrPut(this) {
val tableField = getAnnotation<TableField>()
try {
val tableInfo = TableInfoHelper.getTableInfo(declaringClass)
tableInfo?.fieldList?.firstOrNull {
it.field == this
}?.let { tableFieldInfo ->
FieldData(
field = tableFieldInfo.field,
name = "${tableInfo.tableName}.${tableFieldInfo.column}",
selectionName = "${tableInfo.tableName}.${tableFieldInfo.column} as ${name.sqlName}",
tableField = getAnnotation(),
column = tableFieldInfo.column,
)
} ?: run {
if (tableInfo != null) {
FieldData(
field = this,
name = "${tableInfo.tableName}.${name.sqlName}",
selectionName = "${tableInfo.tableName}.${name.sqlName}",
tableField = tableField,
column = tableField?.value ?: name.sqlName,
)
} else null
}
} catch (e: Throwable) {
null
} ?: run {
val tableName = declaringClass.tableName
FieldData(
field = this,
name = "$tableName.${tableField?.value ?: name.sqlName}",
selectionName = if (tableField == null) {
"$tableName.${name.sqlName}"
} else {
"$tableName.${tableField.value} as ${name.sqlName}"
},
tableField = tableField,
column = tableField?.value ?: name.sqlName,
)
}
}
fun KProperty<*>.getFieldData(): FieldData? {
return kPropertyFieldDataMap.getOrPut(this) {
StrongReference(javaField?.getFieldData() ?: run {
val owner = owner ?: return@run null
val enhanceField = findAnnotation<EnhanceField>() ?: return@run null
if (enhanceField.field.isBlank()) {
return@run null
}
owner.kotlin.allMemberPropertiesSequence.firstOrNull {
it.name == enhanceField.field
}?.getFieldData()
})
}?.r
}
fun getAllSelectionFieldArray(clazz: Class<*>): Array<out String> = allSelectionFieldMap.getOrPut(clazz) {
clazz.allFieldsSequence.filterNotExists.map {
it.getFieldData().column
}.toList().toTypedArray()
}
fun getAllSelectionField(clazz: Class<*>): List<String> = allSelectionFieldMap.getOrPut(clazz) {
clazz.allFieldsSequence.filterNotExists.map {
it.getFieldData().column
}.toList().toTypedArray()
}.asList()
fun getSelectionField(vararg columns: KProperty<*>): Sequence<String> = getSelectionField(columns.asSequence())
fun getSelectionField(columns: Iterable<KProperty<*>>): Sequence<String> = getSelectionField(columns.asSequence())
fun getSelectionField(columns: Sequence<KProperty<*>>): Sequence<String> = columns.map {
it.getFieldData()
}.filterNotNull().map {
it.column
}

View File

@ -0,0 +1,43 @@
package cn.tursom.database.mybatisplus
import cn.tursom.core.allFieldsSequence
import cn.tursom.core.uncheckedCast
import com.baomidou.mybatisplus.core.conditions.Wrapper
import com.baomidou.mybatisplus.core.conditions.update.Update
import java.sql.SQLException
import kotlin.reflect.KProperty1
import kotlin.reflect.jvm.javaField
@MybatisPlusEnhanceDslMaker
interface UpdateEnhance<T, out Children : Wrapper<T>> {
val update: Update<out Children, String> get() = uncheckedCast()
infix fun KProperty1<T, *>.set(
value: Any?,
): Children {
if (getFieldData()?.exist == false) {
logger.warn(
"cannot get field data for {}, javaField: {}, kPropertyFieldDataMap: {}",
this, javaField, kPropertyFieldDataMap[this]
)
throw SQLException("using non exist field $name")
}
return update.set(getFieldData()!!.name, value).uncheckedCast()
}
fun set(vararg values: Pair<KProperty1<T, *>, Any?>): Children {
values.forEach { (column, value) ->
column set value
}
return uncheckedCast()
}
fun set(value: T?): Children {
value ?: return uncheckedCast()
value.javaClass.allFieldsSequence.filterNotExists.forEach { field ->
field.isAccessible = true
update.set(field.getFieldData().name, field.get(value))
}
return uncheckedCast()
}
}

View File

@ -3,13 +3,14 @@
* @author 王景阔
*/
@file:Suppress("unused")
@file:OptIn(UncheckedCast::class)
package cn.tursom.database
package cn.tursom.database.mybatisplus
import cn.tursom.core.UncheckedCast
import cn.tursom.core.allFieldsSequence
import cn.tursom.core.uncheckedCast
import cn.tursom.database.annotations.Getter
import cn.tursom.log.impl.Slf4jImpl
import com.baomidou.mybatisplus.annotation.TableField
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper
import com.baomidou.mybatisplus.core.conditions.ISqlSegment
import com.baomidou.mybatisplus.core.conditions.Wrapper
@ -20,40 +21,53 @@ import com.baomidou.mybatisplus.core.conditions.query.Query
import com.baomidou.mybatisplus.core.conditions.update.Update
import com.baomidou.mybatisplus.core.toolkit.Constants
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.sql.SQLException
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.javaGetter
val logger = Slf4jImpl.getLogger("cn.tursom.database")
val select: Query<*, *, Any>.(Array<out Any>) -> Any = Query<*, *, Any>::select
val selectMethod: Query<*, *, Any>.(Array<out Any>) -> Any = Query<*, *, Any>::select
@Suppress("UNCHECKED_CAST")
fun <T, Children : Wrapper<T>, Q> Query<Children, T, Q>.select(
columns: Array<out Q>,
): Children = (select as Query<Children, T, Q>.(Array<out Q>) -> Children)(columns)
inline fun <reified T, Children : Wrapper<T>, Q> Query<Children, T, Q>.select(
columns: Collection<String>,
): Children = select(T::class.java) {
it.column in columns
}
@Suppress("UNCHECKED_CAST")
inline fun <reified T, Children : Wrapper<T>, Q> Query<Children, T, Q>.select(
columns: Sequence<String>,
): Children = select(columns.toSet())
/**
* QueryWrapper<T>().select(T::fieldName, value)
*/
inline fun <reified T, Children : Wrapper<T>> Query<Children, T, String>.select(
vararg columns: KProperty1<T, *>,
): Children = select(columns.tableField)
): Children = select(columns = columns as Array<out KProperty<*>>)
fun <T, Children : Wrapper<T>> Query<Children, T, String>.select(
inline fun <reified T, Children : Wrapper<T>> Query<Children, T, String>.select(
vararg columns: KProperty<*>,
): Children = fullSelect(*columns)
): Children = select(columns.mapNotNull {
it.getFieldData()?.field?.name
}.toSet())
fun <T, Children : Wrapper<T>> Query<Children, T, String>.fullSelect(
inline fun <reified T, Children : Wrapper<T>> Query<Children, T, String>.fullSelect(
vararg columns: KProperty<*>,
): Children = select(columns.fullTableField)
): Children = selectMethod(uncheckedCast(), columns.mapNotNull {
it.getFieldData()?.selectionName
}.toTypedArray()).uncheckedCast()
inline fun <reified T, Children : Wrapper<T>> Query<Children, T, String>.joinSelect(
vararg columns: KProperty<*>,
): Children = fullSelect(columns = columns)
/**
* QueryWrapper<T>().eq(T::fieldName, value)
@ -61,26 +75,37 @@ fun <T, Children : Wrapper<T>> Query<Children, T, String>.fullSelect(
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.eq(
column: KProperty1<T, *>,
value: Any,
): Children = eq(column.directTableField, value)
): Children = eq(column.getFieldData()!!.name, value)
fun <T, Children : Wrapper<T>> Compare<Children, String>.eq(
column: KProperty<*>,
value: Any,
): Children = eq(column.directTableField, value)
): Children = eq(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.eq(
column: Pair<KProperty1<T, *>, Any?>,
): Children = eq(column.first.directTableField, column.second)
): Children = eq(column.first.getFieldData()!!.name, column.second)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.eq(
vararg pair: Pair<KProperty1<T, *>, Any?>,
): Children = allEq(pair.tableField)
): Children = eq(pair.asSequence())
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.eq(
pair: Collection<Pair<KProperty1<T, *>, Any>>,
): Children = allEq(pair.tableField)
): Children = eq(pair.asSequence())
fun <Children> Compare<Children, String>.eq(vararg pair: Pair<KProperty<*>, *>): Children = allEq(pair.fullTableField)
fun <Children> Compare<Children, String>.eq(
pair: Sequence<Pair<KProperty<*>, *>>,
): Children = allEq(
pair.mapNotNull { (property, value) ->
val fieldData = property.getFieldData() ?: return@mapNotNull null
fieldData.name to value
}.associate {
it
}
)
fun <Children> Compare<Children, String>.eq(vararg pair: Pair<KProperty<*>, *>): Children = eq(pair.asSequence())
inline fun <reified T : Any, Children : Wrapper<T>> Compare<Children, String>.eq(entity: T): Children {
val eqs = LinkedList<Pair<KProperty1<T, *>, Any>>()
@ -91,6 +116,18 @@ inline fun <reified T : Any, Children : Wrapper<T>> Compare<Children, String>.eq
return eq(eqs)
}
@JvmName("eqMapEntry")
fun <Children> Compare<Children, String>.eq(
pair: Sequence<Map.Entry<KProperty<*>, *>>,
): Children = allEq(
pair.mapNotNull { (property, value) ->
val fieldData = property.getFieldData() ?: return@mapNotNull null
fieldData.name to value
}.associate {
it
}
)
/**
* QueryWrapper<T>().allEq(mapOf(
* T::fieldName1 to value1,
@ -99,144 +136,141 @@ inline fun <reified T : Any, Children : Wrapper<T>> Compare<Children, String>.eq
* ))
*/
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.allEq(map: Map<out KProperty1<T, *>, *>): Children =
allEq(map.tableField)
eq(map.asSequence())
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.allEq(vararg pair: Pair<KProperty1<T, *>, *>): Children =
allEq(pair.tableField)
eq(pair.asSequence())
fun <Children> Compare<Children, String>.allEq(vararg pair: Pair<KProperty<*>, *>): Children =
allEq(pair.fullTableField)
eq(pair.asSequence())
fun <Children> Compare<Children, String>.allFullEq(vararg pair: Pair<KProperty<*>, *>): Children =
allEq(pair.fullTableField)
eq(pair.asSequence())
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.ne(
column: KProperty1<T, *>,
value: Any?,
): Children = ne(column.directTableField, value)
): Children = ne(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.gt(
column: KProperty1<T, *>,
value: Any?,
): Children = gt(column.directTableField, value)
): Children = gt(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.ge(
column: KProperty1<T, *>,
value: Any?,
): Children = ge(column.directTableField, value)
): Children = ge(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.lt(
column: KProperty1<T, *>,
value: Any?,
): Children = lt(column.directTableField, value)
): Children = lt(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.le(
column: KProperty1<T, *>,
value: Any?,
): Children = le(column.directTableField, value)
): Children = le(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.between(
column: KProperty1<T, *>,
val1: Any?,
val2: Any?,
): Children = between(column.directTableField, val1, val2)
): Children = between(column.getFieldData()!!.name, val1, val2)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.notBetween(
column: KProperty1<T, *>,
val1: Any?,
val2: Any?,
): Children = notBetween(column.directTableField, val1, val2)
): Children = notBetween(column.getFieldData()!!.name, val1, val2)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.like(
column: KProperty1<T, *>,
value: Any?,
): Children = like(column.directTableField, value)
): Children = like(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.notLike(
column: KProperty1<T, *>,
value: Any?,
): Children = notLike(column.directTableField, value)
): Children = notLike(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.likeLeft(
column: KProperty1<T, *>,
value: Any?,
): Children = likeLeft(column.directTableField, value)
): Children = likeLeft(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Compare<Children, String>.likeRight(
column: KProperty1<T, *>,
value: Any?,
): Children = likeRight(column.directTableField, value)
): Children = likeRight(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.having(
column: KProperty1<T, *>,
vararg value: Any?,
): Children = having(column.directTableField, value)
): Children = having(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.isNull(column: KProperty1<T, *>): Children =
isNull(column.directTableField)
isNull(column.getFieldData()!!.name)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.isNotNull(column: KProperty1<T, *>): Children =
isNotNull(column.directTableField)
isNotNull(column.getFieldData()!!.name)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.`in`(
column: KProperty1<T, *>,
value: Any?,
): Children = `in`(column.directTableField, value)
): Children = `in`(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.`in`(
column: KProperty1<T, *>,
value: Collection<Any?>,
): Children = `in`(column.directTableField, value)
): Children = `in`(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.`in`(
column: KProperty1<T, *>,
vararg value: Any,
): Children = `in`(column.directTableField, value)
): Children = `in`(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.notIn(
column: KProperty1<T, *>,
value: Collection<Any?>,
): Children = notIn(column.directTableField, value)
): Children = notIn(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.notIn(
column: KProperty1<T, *>,
vararg value: Any,
): Children = notIn(column.directTableField, value)
): Children = notIn(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.inSql(
column: KProperty1<T, *>,
value: String?,
): Children = inSql(column.directTableField, value)
): Children = inSql(column.getFieldData()!!.name, value)
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.notInSql(
column: KProperty1<T, *>,
value: String?,
): Children = notInSql(column.directTableField, value)
): Children = notInSql(column.getFieldData()!!.name, value)
// val groupBy: Func<*, String>.(Array<out String>) -> Any = Func<*, String>::groupBy
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.groupBy(column: KProperty1<T, *>): Children =
groupBy(column.directTableField)
groupBy(column.getFieldData()!!.name)
@Suppress("UNCHECKED_CAST")
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.groupBy(vararg column: KProperty1<T, *>): Children =
groupBy(column.directTableField.asList()) as Children
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.groupBy(vararg columns: KProperty1<T, *>): Children =
groupBy(columns.map { column -> column.getFieldData()!!.name }) as Children
// val orderByAsc: Func<*, String>.(Array<out String>) -> Any = Func<*, String>::orderByAsc
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.orderByAsc(column: KProperty1<T, *>): Children =
orderByAsc(column.directTableField)
orderByAsc(column.getFieldData()!!.name)
@Suppress("UNCHECKED_CAST")
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.orderByAsc(vararg column: KProperty1<T, *>): Children =
orderByAsc(column.directTableField.asList()) as Children
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.orderByAsc(vararg columns: KProperty1<T, *>): Children =
orderByAsc(columns.map { column -> column.getFieldData()!!.name }) as Children
// val orderByDesc: Func<*, String>.(Array<out String>) -> Any = Func<*, String>::orderByDesc
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.orderByDesc(column: KProperty1<T, *>): Children =
orderByDesc(column.directTableField)
orderByDesc(column.getFieldData()!!.name)
@Suppress("UNCHECKED_CAST")
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.orderByDesc(vararg column: KProperty1<T, *>): Children =
orderByDesc(column.directTableField.asList()) as Children
inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.orderByDesc(vararg columns: KProperty1<T, *>): Children =
orderByDesc(columns.map { column -> column.getFieldData()!!.name }) as Children
/**
@ -248,27 +282,24 @@ inline fun <reified T, Children : Wrapper<T>> Func<Children, String>.orderByDesc
*/
fun <Children> Join<Children>.limit1(): Children = last("LIMIT 1")
inline fun <reified T, Children : Wrapper<T>> Update<Children, String>.set(
fun <Children> Join<Children>.limit(count: Int): Children = last("LIMIT $count")
fun <Children> Join<Children>.limit(start: Int, count: Int): Children = last("LIMIT $start, $count")
fun <T, Children : Wrapper<T>> Update<Children, String>.set(
column: KProperty1<T, *>,
value: Any?,
): Children {
if (column.javaField == null || Modifier.isTransient(column.javaField?.modifiers ?: Modifier.TRANSIENT) ||
column.javaField?.getAnnotation(TableField::class.java)?.exist == false
) {
return uncheckedCast()
if (column.getFieldData()?.exist == false) {
logger.warn(
"cannot get field data for {}, javaField: {}, kPropertyFieldDataMap: {}",
column, column.javaField, kPropertyFieldDataMap[column]
)
throw SQLException("using non exist field ${column.name}")
}
val getter = column.findAnnotation<Getter>()
val v = if (getter == null || !(value != null && getter.getterType.isInstance(value))) {
value
} else {
val getterMethod = column.javaGetter!!.declaringClass.getDeclaredMethod(getter.getter, getter.getterType.java)
getterMethod.isAccessible = true
getterMethod.invoke(null, value)
}
return set(column.directTableField, v)
return set(column.getFieldData()!!.name, value)
}
inline fun <reified T, Children : Wrapper<T>> Update<Children, String>.set(vararg values: Pair<KProperty1<T, *>, Any?>): Children {
fun <T, Children : Wrapper<T>> Update<Children, String>.set(vararg values: Pair<KProperty1<T, *>, Any?>): Children {
var children: Children? = null
values.forEach { (column, value) ->
set(column, value).let {
@ -278,23 +309,13 @@ inline fun <reified T, Children : Wrapper<T>> Update<Children, String>.set(varar
return children ?: uncheckedCast()
}
inline fun <reified T : Any, Children : Wrapper<T>> Update<Children, String>.set(value: T): Children {
var children: Children? = null
value::class.memberProperties
.uncheckedCast<Collection<KProperty1<T, *>>>()
.filter {
it.javaField != null &&
(it.findAnnotation() ?: it.javaField?.getAnnotation(Transient::class.java)) == null &&
!Modifier.isTransient(it.javaField?.modifiers ?: Modifier.TRANSIENT) &&
it.javaField?.getAnnotation(TableField::class.java)?.exist != false
}
.forEach { property ->
property.isAccessible = true
set(property, property.get(value) ?: return@forEach).let {
if (children == null) children = it
}
}
return children ?: uncheckedCast()
fun <Children> Update<Children, String>.set(value: Any?): Children {
value ?: return uncheckedCast()
value.javaClass.allFieldsSequence.filterNotExists.forEach { field ->
field.isAccessible = true
set(field.getFieldData().name, field.get(value))
}
return uncheckedCast()
}
object Regexp : ISqlSegment {
@ -310,9 +331,10 @@ object WrapperEnhance : AbstractWrapper<Any, String, WrapperEnhance>() {
initNeed()
}
fun <T, W : AbstractWrapper<T, String, W>> regex(wrapper: W, column: String, value: Any): W {
fun <T, W : AbstractWrapper<T, String, out W>> regex(wrapper: W, column: String, value: Any): W {
wrapper.expression.add(ISqlSegment { columnToString(column) }, Regexp, ISqlSegment {
val genParamName = Constants.WRAPPER_PARAM + (paramNameSeqField.get(wrapper) as AtomicInteger).incrementAndGet()
val genParamName =
Constants.WRAPPER_PARAM + (paramNameSeqField.get(wrapper) as AtomicInteger).incrementAndGet()
wrapper.paramNameValuePairs[genParamName] = value
"#{${Constants.WRAPPER}${Constants.WRAPPER_PARAM_MIDDLE}$genParamName}"
})
@ -325,8 +347,8 @@ fun <T, W : AbstractWrapper<T, String, W>> W.regex(column: String, value: Any):
inline fun <reified T, W : AbstractWrapper<T, String, W>> W.regex(
column: KProperty1<T, *>, value: Any,
): W = WrapperEnhance.regex(this, column.directTableField, value)
): W = WrapperEnhance.regex(this, column.getFieldData()!!.name, value)
inline fun <reified T, W : AbstractWrapper<T, String, W>> W.regex(
column: KProperty1<T, *>, regex: Regex,
): W = WrapperEnhance.regex(this, column.directTableField, regex.toString())
): W = WrapperEnhance.regex(this, column.getFieldData()!!.name, regex.toString())

View File

@ -1,5 +1,6 @@
package cn.tursom.web.router
import cn.tursom.core.allMethodsSequence
import cn.tursom.core.buffer.ByteBuffer
import cn.tursom.core.json.JsonWorkerImpl
import cn.tursom.core.lambda
@ -55,7 +56,9 @@ open class RoutedHttpHandler(
if ((target.isEmpty())) {
addRouter(this)
} else {
target.forEach(::addRouter)
target.forEach {
addRouter(it)
}
}
}
@ -90,9 +93,8 @@ open class RoutedHttpHandler(
open fun addRouter(handler: Any) {
log?.info("add router {}", handler)
@Suppress("LeakingThis")
val clazz = handler.javaClass
clazz.methods.forEach { method ->
handler.javaClass.allMethodsSequence.forEach { method ->
method.isAccessible = true
method.parameterTypes.let {
if (!(it.size == 1 && HttpContent::class.java.isAssignableFrom(it[0])) && it.isNotEmpty()) {
return@forEach
@ -134,7 +136,7 @@ open class RoutedHttpHandler(
val mapping = obj::class.java.getAnnotation(Mapping::class.java)?.route ?: arrayOf("")
method.annotations.forEach { annotation ->
val (routes, router) = getRoutes(annotation) ?: return@forEach
log?.info("mapping {} => {}", routes, method)
log?.info("mapping {} {} => {}", getRouteMethod(annotation), routes, method)
routes.forEach { route ->
if (mapping.isEmpty()) {
addRouter(obj, method, route, router)
@ -211,7 +213,7 @@ open class RoutedHttpHandler(
method.annotations.forEach { annotation ->
val (routes, router) = getRoutes(annotation) ?: return@forEach
routes.forEach { route ->
log?.info("delete route {} => {}", route, method)
log?.info("delete route {} {} => {}", getRouteMethod(annotation), route, method)
if (mapping.isEmpty()) {
deleteMapping(obj, route, router)
} else mapping.forEach {
@ -228,8 +230,22 @@ open class RoutedHttpHandler(
}
}
protected fun getRouteMethod(annotation: Annotation): String? = when (annotation) {
is Mapping -> annotation.method.ifEmpty { annotation.methodEnum.method }
is GetMapping -> "GET"
is PostMapping -> "POST"
is PutMapping -> "PUT"
is DeleteMapping -> "DELETE"
is PatchMapping -> "PATCH"
is TraceMapping -> "TRACE"
is HeadMapping -> "HEAD"
is OptionsMapping -> "OPTIONS"
is ConnectMapping -> "CONNECT"
else -> null
}
protected fun getRoutes(annotation: Annotation) = when (annotation) {
is Mapping -> annotation.route to getRouter(annotation.method.let { if (it.isEmpty()) annotation.methodEnum.method else it })
is Mapping -> annotation.route to getRouter(annotation.method.ifEmpty { annotation.methodEnum.method })
is GetMapping -> annotation.route to getRouter("GET")
is PostMapping -> annotation.route to getRouter("POST")
is PutMapping -> annotation.route to getRouter("PUT")
@ -245,7 +261,7 @@ open class RoutedHttpHandler(
protected fun getRouter(method: String): Router<Pair<Any?, (HttpContent) -> Any?>> = when {
method.isEmpty() -> router
else -> {
val upperCaseMethod = method.toUpperCase()
val upperCaseMethod = method.uppercase()
var router = routerMap[upperCaseMethod]
if (router == null) {
router = routerMaker()

View File

@ -7,6 +7,7 @@ plugins {
dependencies {
implementation(project(":"))
implementation(project(":ts-web"))
implementation(project(":ts-core"))
implementation(project(":ts-core:ts-buffer"))
implementation(project(":ts-core:ts-json"))
implementation(group = "org.slf4j", name = "slf4j-api", version = "1.7.32")

View File

@ -1,5 +1,6 @@
package cn.tursom.web.router
import cn.tursom.core.allMethodsSequence
import cn.tursom.web.HttpContent
import cn.tursom.web.MutableHttpContent
import cn.tursom.web.mapping.*
@ -11,16 +12,18 @@ import kotlinx.coroutines.launch
import org.slf4j.LoggerFactory
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.util.*
import kotlin.reflect.KCallable
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.jvm.jvmErasure
import kotlin.reflect.jvm.kotlinFunction
@Suppress("ProtectedInFinal", "unused", "MemberVisibilityCanBePrivate")
open class AsyncRoutedHttpHandler(
vararg target: Any,
routerMaker: () -> Router<Pair<Any?, (HttpContent) -> Any?>> = { SimpleRouter() },
val asyncRouterMaker: () -> Router<Pair<Any?, suspend (HttpContent) -> Unit>> = { SimpleRouter() }
) : RoutedHttpHandler(target, routerMaker) {
) : RoutedHttpHandler(target = target, routerMaker = routerMaker) {
protected val asyncRouter: Router<Pair<Any?, suspend (HttpContent) -> Unit>> = asyncRouterMaker()
protected val asyncRouterMap: HashMap<String, Router<Pair<Any?, suspend (HttpContent) -> Unit>>> = HashMap()
@ -82,15 +85,21 @@ open class AsyncRoutedHttpHandler(
override fun addRouter(handler: Any) {
super.addRouter(handler)
handler::class.members.forEach { member ->
if (member.isSuspend) {
member.parameters.let {
handler.javaClass.allMethodsSequence.forEach { method ->
val function = try {
method.kotlinFunction ?: return@forEach
} catch (e: Exception) {
return@forEach
}
method.isAccessible = true
if (function.isSuspend) {
function.parameters.let {
if (it.size != 1 && !(it.size == 2 && HttpContent::class.java.isAssignableFrom(it[1].type.jvmErasure.java))) {
return@forEach
}
}
log?.trace("mapping {} {}", member, member.parameters)
insertMapping(handler, member)
log?.trace("mapping {} {}", function, function.parameters)
insertMapping(handler, function)
}
}
}
@ -100,7 +109,7 @@ open class AsyncRoutedHttpHandler(
method.annotations.forEach { annotation ->
val (routes, router) = getAsyncRoutes(annotation) ?: return@forEach
@Suppress("DuplicatedCode")
log?.info("mapping {} => {}", routes, method)
log?.info("mapping {} {} => {}", getRouteMethod(annotation), routes, method)
routes.forEach { route ->
if (mapping.isEmpty()) {
addRouter(obj, method, route, router)
@ -174,7 +183,7 @@ open class AsyncRoutedHttpHandler(
protected fun getAsyncRouter(method: String): Router<Pair<Any?, suspend (HttpContent) -> Unit>> = when {
method.isEmpty() -> asyncRouter
else -> {
val upperCaseMethod = method.toUpperCase()
val upperCaseMethod = method.uppercase(Locale.getDefault())
var router = asyncRouterMap[upperCaseMethod]
if (router == null) {
router = asyncRouterMaker()