mirror of
https://github.com/tursom/TursomServer.git
synced 2025-03-25 09:00:07 +08:00
开始重构database模块
This commit is contained in:
parent
9f47f1d70d
commit
5ffbe0e728
@ -1,12 +1,10 @@
|
||||
dependencies {
|
||||
implementation project(":")
|
||||
implementation project(":utils")
|
||||
// mongodb
|
||||
implementation "org.mongodb:mongo-java-driver:+"
|
||||
// cn.tusom.database.sqlite 依赖的库
|
||||
implementation 'org.xerial:sqlite-jdbc:3.21.0.1'
|
||||
// cn.tusom.database.mysql 依赖的库
|
||||
implementation group: 'mysql', name: 'mysql-connector-java', version: '+'
|
||||
//implementation project(":utils")
|
||||
//// cn.tusom.database.sqlite 依赖的库
|
||||
//implementation 'org.xerial:sqlite-jdbc:3.21.0.1'
|
||||
//// cn.tusom.database.mysql 依赖的库
|
||||
//implementation group: 'mysql', name: 'mysql-connector-java', version: '+'
|
||||
// 数据库与序列化部分可选
|
||||
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion
|
||||
api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
package cn.tursom.database.async
|
||||
|
||||
import io.vertx.core.json.JsonObject
|
||||
import io.vertx.ext.jdbc.JDBCClient
|
||||
import io.vertx.ext.sql.SQLConnection
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import java.sql.Connection
|
||||
import javax.annotation.PostConstruct
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class AsyncJdbcUtils {
|
||||
private val regexp = object : org.sqlite.Function() {
|
||||
override fun xFunc() {
|
||||
val regex = Regex(value_text(0) ?: "")
|
||||
val value = value_text(1) ?: ""
|
||||
result(if (regex.containsMatchIn(value)) 1 else 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Value("\${jdbc.providerClass}")
|
||||
var providerClass: String? = null
|
||||
@Value("\${jdbc.rowStreamFetchSize}")
|
||||
var rowStreamFetchSize: String? = null
|
||||
|
||||
@Value("\${jdbc.url}")
|
||||
var url: String = ""
|
||||
@Value("\${jdbc.driver}")
|
||||
var driver: String = ""
|
||||
@Value("\${jdbc.driver}")
|
||||
var user: String? = null
|
||||
@Value("\${jdbc.driver}")
|
||||
var password: String? = null
|
||||
|
||||
@Value("\${jdbc.maxPoolSize}")
|
||||
var maxPoolSize: Int? = null
|
||||
@Value("\${jdbc.initialPoolSize}")
|
||||
var initialPoolSize: Int? = null
|
||||
@Value("\${jdbc.minPoolSize}")
|
||||
var minPoolSize: Int? = null
|
||||
/**
|
||||
* 预处理SQL语句最小缓存数,默认 0
|
||||
*/
|
||||
@Value("\${jdbc.maxStatements}")
|
||||
var maxStatements: Int? = null
|
||||
/**
|
||||
* 每个数据库连接的最大预处理SQL缓存数,默认0
|
||||
*/
|
||||
@Value("\${jdbc.maxStatementsPerConnection}")
|
||||
var maxStatementsPerConnection: Int? = null
|
||||
/**
|
||||
* 空闲连接保留时间,默认600000 (10分钟)
|
||||
*/
|
||||
@Value("\${jdbc.maxIdleTime}")
|
||||
var maxIdleTime: Int = 600000
|
||||
|
||||
/**
|
||||
* 是否自动提交,对 Hikari 有效
|
||||
*/
|
||||
@Value("\${jdbc.autoCommit}")
|
||||
var autoCommit: Boolean = false
|
||||
|
||||
/**
|
||||
* Hikari 连接最大时间
|
||||
*/
|
||||
@Value("\${jdbc.connectionTimeout}")
|
||||
var connectionTimeout: Int = 30000
|
||||
|
||||
@Value("\${jdbc.poolName}")
|
||||
var poolName: String? = null
|
||||
|
||||
private var client: JDBCClient? = null
|
||||
|
||||
private var injectSqliteRegex: Boolean = false
|
||||
|
||||
@PostConstruct
|
||||
fun initClient() {
|
||||
client?.close()
|
||||
val config = JsonObject()
|
||||
injectSqliteRegex = try {
|
||||
url.split(':')[1] == "sqlite"
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
providerClass?.let { config.put("provider_class", it) }
|
||||
rowStreamFetchSize?.let { config.put("row_stream_fetch_size", it) }
|
||||
|
||||
config.put("url", url)
|
||||
config.put("jdbcUrl", url)
|
||||
config.put("driver_class", driver)
|
||||
config.put("driverClassName", driver)
|
||||
user?.let { config.put("user", it) }
|
||||
password?.let { config.put("password", it) }
|
||||
|
||||
maxPoolSize?.let { config.put("max_pool_size", it) }
|
||||
maxPoolSize?.let { config.put("maximumPoolSize", it) }
|
||||
initialPoolSize?.let { config.put("initial_pool_size", it) }
|
||||
minPoolSize?.let { config.put("min_pool_size", it) }
|
||||
maxStatements?.let { config.put("max_statements", it) }
|
||||
maxStatementsPerConnection?.let { config.put("max_statements_per_connection", it) }
|
||||
config.put("max_idle_time", maxIdleTime)
|
||||
config.put("idleTimeout", maxIdleTime)
|
||||
config.put("autoCommit", autoCommit)
|
||||
config.put("connectionTimeout", connectionTimeout)
|
||||
poolName?.let { config.put("poolName", it) }
|
||||
|
||||
client = JDBCClient.createShared(vertx, config, url)
|
||||
}
|
||||
|
||||
suspend fun getConnection(): SQLConnection {
|
||||
return suspendCoroutine { cont ->
|
||||
client?.getConnection {
|
||||
if (it.failed()) {
|
||||
cont.resumeWithException(it.cause())
|
||||
} else {
|
||||
if (injectSqliteRegex) {
|
||||
val conn = it.result()
|
||||
// 我们要利用反射强行把我们需要的方法注入进去
|
||||
val connField = conn.javaClass.getDeclaredField("conn")
|
||||
connField.isAccessible = true
|
||||
val fieldConn = connField.get(conn)
|
||||
val innerField = fieldConn.javaClass.getDeclaredField("inner")
|
||||
innerField.isAccessible = true
|
||||
org.sqlite.Function.create(innerField.get(fieldConn) as Connection, "REGEXP", regexp)
|
||||
}
|
||||
cont.resume(it.result())
|
||||
}
|
||||
} ?: cont.resumeWithException(NullPointerException())
|
||||
}
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
package cn.tursom.database.async
|
||||
|
||||
import cn.tursom.database.SqlAdapter
|
||||
import cn.tursom.database.SqlUtils.constructor
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.SqlUtils.ignored
|
||||
import cn.tursom.database.annotation.NotNull
|
||||
import io.vertx.core.json.JsonArray
|
||||
import io.vertx.ext.sql.ResultSet
|
||||
import sun.misc.Unsafe
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Method
|
||||
import java.sql.SQLException
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS", "MemberVisibilityCanBePrivate")
|
||||
class AsyncSqlAdapter<T>(
|
||||
@Suppress("MemberVisibilityCanBePrivate") val clazz: Class<T>,
|
||||
var autoClear: Boolean = true,
|
||||
private val adapter: (ArrayList<T>.(
|
||||
resultSet: ResultSet,
|
||||
fieldList: List<SqlAdapter.FieldData>
|
||||
) -> Unit)? = null
|
||||
) : ArrayList<T>() {
|
||||
val fieldNameMap: Map<String, FieldCache> = run {
|
||||
val map = HashMap<String, FieldCache>()
|
||||
clazz.declaredFields.forEach {
|
||||
if (it.ignored) return@forEach
|
||||
it.isAccessible = true
|
||||
val constructorAnnotation = it.constructor
|
||||
val constructor = try {
|
||||
clazz.getDeclaredMethod(constructorAnnotation, String::class.java)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
val advanceConstructor = try {
|
||||
clazz.getDeclaredMethod(constructorAnnotation, JsonArray::class.java, Int::class.java)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
map[it.fieldName] = FieldCache(it, it.type, advanceConstructor, constructor)
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
open fun adapt(result: ResultSet) {
|
||||
if (autoClear) clear()
|
||||
|
||||
val fieldList = ArrayList<FieldData>()
|
||||
result.columnNames.forEachIndexed { index, fieldName ->
|
||||
try {
|
||||
val field = fieldNameMap[fieldName] ?: return@forEachIndexed
|
||||
fieldList.add(FieldData(index, field))
|
||||
} catch (e: SQLException) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
result.results.forEach {
|
||||
adaptOnce(it, fieldList)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
open fun adaptOnce(result: JsonArray, fieldList: List<FieldData>) {
|
||||
// 绕过构造函数获取变量0
|
||||
val bean = unsafe.allocateInstance(clazz) as T
|
||||
fieldList.forEach { (index, fieldCache) ->
|
||||
val (field, beanType, advanceSetter, setter) = fieldCache
|
||||
try {
|
||||
if (advanceSetter != null) {
|
||||
// 如果你有能力直接从ResultSet里面取出数据,那就随君便
|
||||
val value = try {
|
||||
advanceSetter.invoke(bean, result, index)!!
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.targetException
|
||||
}
|
||||
field.set(bean, value)
|
||||
} else {
|
||||
result.getValue(index)?.let { value ->
|
||||
// 让我们把数据喂进去
|
||||
field.set(bean, handleCast(bean, field, beanType, value, setter))
|
||||
}
|
||||
}
|
||||
} catch (e: SQLException) {
|
||||
e.printStackTrace()
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
add(bean)
|
||||
}
|
||||
|
||||
private fun handleCast(
|
||||
bean: T,
|
||||
field: Field,
|
||||
beanType: Class<*>,
|
||||
value: Any,
|
||||
setter: Method?
|
||||
): Any? {
|
||||
val dbType = value.javaClass
|
||||
return when {
|
||||
setter != null -> try {
|
||||
setter.invoke(bean, value.toString())
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.targetException
|
||||
}
|
||||
|
||||
beanType == java.lang.Float::class.java -> when (dbType) {
|
||||
java.lang.Double::class.java -> (value as Double).toFloat()
|
||||
else -> //检查是否可以为空
|
||||
if (field.getAnnotation(NotNull::class.java) != null) {
|
||||
value.toString().toFloat()
|
||||
} else {
|
||||
value.toString().toFloatOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
beanType == java.lang.String::class.java && dbType != java.lang.String::class.java ->
|
||||
value.toString()
|
||||
|
||||
beanType == java.lang.Boolean::class.java -> when {
|
||||
field.getAnnotation(NotNull::class.java) != null -> {
|
||||
value.toString().toBoolean()
|
||||
}
|
||||
else -> try {
|
||||
value.toString().toBoolean()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
else -> value
|
||||
}
|
||||
}
|
||||
|
||||
data class FieldData(
|
||||
val index: Int,
|
||||
val field: FieldCache
|
||||
)
|
||||
|
||||
data class FieldCache(
|
||||
val field: Field,
|
||||
val beanType: Class<*>,
|
||||
val advanceSetter: Method?,
|
||||
val setter: Method?
|
||||
)
|
||||
|
||||
companion object {
|
||||
//利用Unsafe绕过构造函数获取变量
|
||||
private val unsafe by lazy {
|
||||
val field = Unsafe::class.java.getDeclaredField("theUnsafe")
|
||||
//允许通过反射设置属性的值
|
||||
field.isAccessible = true
|
||||
field.get(null) as Unsafe
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package cn.tursom.database.async
|
||||
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.database.clauses.ClauseMaker
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
class AsyncSqlDeleter(
|
||||
private val helper: AsyncSqlHelper,
|
||||
private var table: String? = null,
|
||||
private var where: Clause? = null
|
||||
) {
|
||||
infix fun Class<*>.where(where: ClauseMaker.() -> Clause) {
|
||||
table = tableName
|
||||
this@AsyncSqlDeleter.where = ClauseMaker.where()
|
||||
}
|
||||
|
||||
infix fun KClass<*>.where(where: ClauseMaker.() -> Clause) {
|
||||
table = tableName
|
||||
this@AsyncSqlDeleter.where = ClauseMaker.where()
|
||||
}
|
||||
|
||||
suspend fun delete() = helper.delete(table!!, where)
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
package cn.tursom.database.async
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.database.clauses.ClauseMaker
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import io.vertx.ext.sql.SQLConnection
|
||||
import java.lang.reflect.Field
|
||||
|
||||
interface AsyncSqlHelper {
|
||||
val connection: SQLConnection
|
||||
|
||||
suspend fun doSql(sql: String): Int
|
||||
|
||||
suspend fun createTable(fields: Class<*>)
|
||||
|
||||
suspend fun deleteTable(table: String)
|
||||
suspend fun deleteTable(table: Class<*>) = deleteTable(table.tableName)
|
||||
suspend fun dropTable(table: String)
|
||||
suspend fun dropTable(table: Class<*>) = dropTable(table.tableName)
|
||||
|
||||
suspend fun <T : Any> select(
|
||||
adapter: AsyncSqlAdapter<T>,
|
||||
fields: String = "*",
|
||||
where: String? = null,
|
||||
order: String? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null,
|
||||
table: String = adapter.clazz.tableName
|
||||
): AsyncSqlAdapter<T>
|
||||
|
||||
suspend fun <T : Any> select(
|
||||
adapter: AsyncSqlAdapter<T>,
|
||||
fields: Iterable<String>,
|
||||
where: Clause? = null,
|
||||
order: Field? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null,
|
||||
table: String = adapter.clazz.tableName
|
||||
): AsyncSqlAdapter<T>
|
||||
|
||||
suspend fun insert(value: Any): Int
|
||||
|
||||
suspend fun insert(valueList: Iterable<*>): Int
|
||||
|
||||
suspend fun replace(table: String, value: Any): Int
|
||||
|
||||
suspend fun replace(table: String, valueList: Iterable<*>): Int
|
||||
|
||||
suspend fun update(table: String, set: String, where: String? = null): Int
|
||||
|
||||
suspend fun update(value: Any, where: Clause): Int
|
||||
|
||||
suspend fun delete(table: String, where: String? = null): Int
|
||||
|
||||
suspend fun delete(table: String, where: Clause?): Int
|
||||
|
||||
suspend fun commit()
|
||||
|
||||
suspend fun close()
|
||||
|
||||
|
||||
suspend fun replace(value: Any): Int = replace(value.javaClass.tableName, value)
|
||||
|
||||
suspend fun replace(valueList: Iterable<*>): Int {
|
||||
return replace((valueList.first() ?: return 0).tableName, valueList)
|
||||
}
|
||||
|
||||
suspend fun <T : Any> select(
|
||||
adapter: AsyncSqlAdapter<T>,
|
||||
maker: AsyncSqlSelector<T>.() -> Unit
|
||||
): AsyncSqlAdapter<T> {
|
||||
val selector = AsyncSqlSelector(this, adapter)
|
||||
selector.maker()
|
||||
return selector.select()
|
||||
}
|
||||
|
||||
suspend fun <T : Any> select(
|
||||
adapter: AsyncSqlAdapter<T>,
|
||||
where: Clause,
|
||||
fields: String = "*",
|
||||
order: Field? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null,
|
||||
table: String = adapter.clazz.tableName
|
||||
): AsyncSqlAdapter<T> = select(adapter, fields, where.sqlStr, order?.fieldName, reverse, maxCount, table)
|
||||
|
||||
suspend fun <T : Any> select(
|
||||
clazz: Class<T>,
|
||||
where: Clause,
|
||||
fields: String = "*",
|
||||
order: Field? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null,
|
||||
table: String = clazz.tableName
|
||||
): AsyncSqlAdapter<T> = select(AsyncSqlAdapter(clazz), fields, where.sqlStr, order?.fieldName, reverse, maxCount, table)
|
||||
|
||||
suspend fun <T : Any> select(
|
||||
clazz: Class<T>,
|
||||
fields: Iterable<String> = listOf("*"),
|
||||
where: Clause,
|
||||
order: Field? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null,
|
||||
table: String = clazz.tableName
|
||||
): AsyncSqlAdapter<T> = select(AsyncSqlAdapter(clazz), fields, where, order, reverse, maxCount, table)
|
||||
|
||||
suspend fun delete(
|
||||
clazz: Class<*>,
|
||||
where: String? = null
|
||||
) = delete(clazz.tableName, where)
|
||||
|
||||
suspend fun delete(
|
||||
table: String,
|
||||
where: ClauseMaker.() -> Clause
|
||||
) = delete(table, ClauseMaker.where())
|
||||
|
||||
suspend infix fun delete(helper: AsyncSqlDeleter.() -> Unit): Int {
|
||||
val deleter = AsyncSqlDeleter(this)
|
||||
deleter.helper()
|
||||
return deleter.delete()
|
||||
}
|
||||
|
||||
suspend infix fun update(updater: AsyncSqlUpdater.() -> Unit): Int {
|
||||
val sqlUpdater = AsyncSqlUpdater(this)
|
||||
sqlUpdater.updater()
|
||||
return sqlUpdater.update()
|
||||
}
|
||||
}
|
||||
|
||||
suspend inline fun <reified T : Any> AsyncSqlHelper.select(
|
||||
fields: Iterable<String> = listOf("*"),
|
||||
where: Clause? = null,
|
||||
order: Field? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null
|
||||
): AsyncSqlAdapter<T> = select(AsyncSqlAdapter(T::class.java), fields, where, order, reverse, maxCount)
|
||||
|
||||
suspend inline infix fun <reified T : Any> AsyncSqlHelper.select(
|
||||
noinline maker: AsyncSqlSelector<T>.() -> Unit
|
||||
): AsyncSqlAdapter<T> = select(AsyncSqlAdapter(T::class.java), maker)
|
@ -1,81 +0,0 @@
|
||||
package cn.tursom.database.async
|
||||
|
||||
import cn.tursom.database.*
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.SqlUtils.sqlStr
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.database.clauses.ClauseMaker
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class AsyncSqlSelector<T : Any>(
|
||||
private val helper: AsyncSqlHelper,
|
||||
private val adapter: AsyncSqlAdapter<T>
|
||||
) {
|
||||
private var where: String? = null
|
||||
private var fields: FieldSelector = FieldSelector()
|
||||
private var order: String? = null
|
||||
private var reverse: Boolean = false
|
||||
private var maxCount: Int? = null
|
||||
set(value) {
|
||||
when {
|
||||
value ?: 1 <= 0 -> return
|
||||
else -> field = value
|
||||
}
|
||||
}
|
||||
|
||||
suspend infix fun fields(selector: suspend FieldSelector.() -> Unit) {
|
||||
fields.selector()
|
||||
}
|
||||
|
||||
suspend infix fun where(clause: suspend ClauseMaker.() -> Clause) {
|
||||
where = ClauseMaker.clause().sqlStr
|
||||
}
|
||||
|
||||
suspend fun select() =
|
||||
helper.select(adapter, fields.fieldName ?: "*", where, order, reverse, maxCount)
|
||||
|
||||
infix fun orderBy(field: String) {
|
||||
order = field.sqlStr
|
||||
}
|
||||
|
||||
infix fun orderBy(field: Field) {
|
||||
order = field.fieldName
|
||||
}
|
||||
|
||||
infix fun orderBy(field: KProperty<*>) {
|
||||
order = field.fieldName
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.reverse(reverse: Boolean) {
|
||||
order = fieldName
|
||||
this@AsyncSqlSelector.reverse = reverse
|
||||
}
|
||||
|
||||
infix fun Field.reverse(reverse: Boolean) {
|
||||
order = fieldName
|
||||
this@AsyncSqlSelector.reverse = reverse
|
||||
}
|
||||
|
||||
infix fun reverse(reverse: Boolean) {
|
||||
this.reverse = reverse
|
||||
}
|
||||
|
||||
infix fun limit(maxCount: Int?) {
|
||||
this.maxCount = maxCount
|
||||
}
|
||||
|
||||
infix fun Field.limit(maxCount: Int?) {
|
||||
order = fieldName
|
||||
this@AsyncSqlSelector.maxCount = maxCount
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.limit(maxCount: Int?) {
|
||||
order = fieldName
|
||||
this@AsyncSqlSelector.maxCount = maxCount
|
||||
}
|
||||
|
||||
infix fun Any.limit(maxCount: Int?) {
|
||||
this@AsyncSqlSelector.maxCount = maxCount
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package cn.tursom.database.async
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import cn.tursom.database.SqlUtils.sqlStr
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.database.clauses.ClauseMaker
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class AsyncSqlUpdater(val helper: AsyncSqlHelper) {
|
||||
constructor(helper: AsyncSqlHelper, table: String) : this(helper) {
|
||||
this.table = table
|
||||
}
|
||||
|
||||
private lateinit var table: String
|
||||
private val set = HashMap<String, String>()
|
||||
private var where: String? = null
|
||||
|
||||
infix fun table(table: String) {
|
||||
this.table = table
|
||||
}
|
||||
|
||||
infix fun table(clazz: Class<*>) {
|
||||
table = clazz.tableName
|
||||
}
|
||||
|
||||
infix fun table(clazz: KClass<*>) {
|
||||
table = clazz.tableName
|
||||
}
|
||||
|
||||
infix fun Field.setTo(value: String) {
|
||||
set[fieldName] = value.sqlStr
|
||||
}
|
||||
|
||||
infix fun Field.setTo(value: Any) {
|
||||
set[fieldName] = value.toString()
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.setTo(value: String) {
|
||||
set[fieldName] = value.sqlStr
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.setTo(value: Any) {
|
||||
set[fieldName] = value.toString()
|
||||
}
|
||||
|
||||
infix operator fun Field.plus(value: Any) = +"$fieldName+$value"
|
||||
infix operator fun Field.minus(value: Any) = +"$fieldName-$value"
|
||||
infix operator fun Field.times(value: Any) = +"$fieldName*$value"
|
||||
infix operator fun Field.div(value: Any) = +"$fieldName/$value"
|
||||
infix operator fun Field.rem(value: Any) = +"$fieldName%$value"
|
||||
|
||||
infix operator fun KProperty<*>.plus(value: Any) = +"$fieldName+$value"
|
||||
infix operator fun KProperty<*>.minus(value: Any) = +"$fieldName-$value"
|
||||
infix operator fun KProperty<*>.times(value: Any) = +"$fieldName*$value"
|
||||
infix operator fun KProperty<*>.div(value: Any) = +"$fieldName/$value"
|
||||
infix operator fun KProperty<*>.rem(value: Any) = +"$fieldName%$value"
|
||||
|
||||
infix fun where(clause: ClauseMaker.() -> Clause) {
|
||||
where = ClauseMaker.clause().sqlStr
|
||||
}
|
||||
|
||||
operator fun String.unaryPlus() = StringUnit(this)
|
||||
|
||||
class StringUnit(private val str: String) {
|
||||
operator fun not() = str
|
||||
override fun toString() = str
|
||||
}
|
||||
|
||||
suspend fun update(): Int {
|
||||
val set = StringBuilder()
|
||||
this.set.forEach { (field, value) ->
|
||||
set.append("$field=$value,")
|
||||
}
|
||||
if (set.isNotEmpty()) {
|
||||
set.delete(set.length - 1, set.length)
|
||||
}
|
||||
return helper.update(table, set.toString(), where ?: "")
|
||||
}
|
||||
}
|
@ -1,305 +0,0 @@
|
||||
package cn.tursom.database.async.myqsl
|
||||
|
||||
import cn.tursom.database.*
|
||||
import cn.tursom.database.annotation.FieldType
|
||||
import cn.tursom.database.annotation.ForeignKey
|
||||
import cn.tursom.database.annotation.Getter
|
||||
import cn.tursom.database.annotation.TextLength
|
||||
import cn.tursom.database.async.AsyncSqlAdapter
|
||||
import cn.tursom.database.async.AsyncSqlHelper
|
||||
import cn.tursom.database.async.vertx
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.SqlUtils.fieldStr
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import cn.tursom.database.SqlUtils.valueStr
|
||||
import cn.tursom.database.SqlUtils.fieldValue
|
||||
import cn.tursom.database.SqlUtils.ignored
|
||||
import cn.tursom.database.SqlUtils.appendField
|
||||
import cn.tursom.database.SqlUtils.getAnnotation
|
||||
import io.vertx.core.json.JsonObject
|
||||
import io.vertx.ext.jdbc.JDBCClient
|
||||
import io.vertx.ext.sql.SQLConnection
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.lang.reflect.Field
|
||||
import java.sql.SQLSyntaxErrorException
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class AsyncMySqlHelper(url: String, user: String, password: String, base: String? = null) : AsyncSqlHelper {
|
||||
override suspend fun replace(table: String, value: Any): Int {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override suspend fun replace(table: String, valueList: Iterable<*>): Int {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override val connection = runBlocking {
|
||||
val config = JsonObject()
|
||||
config.put("url", "jdbc:mysql://$url?characterEncoding=utf-8&serverTimezone=UTC")
|
||||
config.put("_driver class", "org.sqlite.JDBC")
|
||||
config.put("user", user)
|
||||
config.put("password", password)
|
||||
suspendCoroutine<SQLConnection> { cont ->
|
||||
JDBCClient.createShared(vertx, config, "$url@$user").getConnection {
|
||||
if (!it.failed()) {
|
||||
cont.resume(it.result())
|
||||
} else {
|
||||
cont.resumeWithException(it.cause())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
var basename: String? = base
|
||||
set(value) {
|
||||
runBlocking {
|
||||
value?.let { base ->
|
||||
doSql("USE $base")
|
||||
field = base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun doSql(sql: String): Int = suspendCoroutine { cont ->
|
||||
connection.execute(sql) {
|
||||
if (it.succeeded()) {
|
||||
cont.resume(1)
|
||||
} else {
|
||||
cont.resumeWithException(it.cause())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun commit() = suspendCoroutine<Unit> { cont ->
|
||||
connection.commit {
|
||||
if (it.succeeded()) {
|
||||
cont.resume(Unit)
|
||||
} else {
|
||||
cont.resumeWithException(it.cause())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据提供的class对象自动化创建表格
|
||||
*/
|
||||
override suspend fun createTable(fields: Class<*>) {
|
||||
createTable(fields.tableName, fields, "InnoDB", "utf8")
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据提供的class对象自动化创建表格
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
suspend fun createTable(table: String, keys: Class<*>, engine: String = "InnoDB", charset: String = "utf8") {
|
||||
doSql(createTableStr(table, keys, engine, charset))
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除表格
|
||||
*/
|
||||
override suspend fun deleteTable(table: String) {
|
||||
doSql("DROP TABLE if exists $table ENGINE = InnoDB DEFAULT CHARSET=utf8;")
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除表格
|
||||
*/
|
||||
override suspend fun dropTable(table: String) {
|
||||
deleteTable(table)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param adapter 用于保存查询结果的数据类,由AsyncSqlAdapter继承而来
|
||||
* @param fields 查询字段
|
||||
* @param where 指定从一个表或多个表中获取数据的条件,Pair左边为字段名,右边为限定的值
|
||||
* @param maxCount 最大查询数量
|
||||
*/
|
||||
override suspend fun <T : Any> select(
|
||||
adapter: AsyncSqlAdapter<T>,
|
||||
fields: Iterable<String>,
|
||||
where: Clause?,
|
||||
order: Field?,
|
||||
reverse: Boolean,
|
||||
maxCount: Int?,
|
||||
table: String
|
||||
): AsyncSqlAdapter<T> {
|
||||
val sql = "SELECT ${fields.fieldStr()} FROM ${table
|
||||
}${if (where != null) " WHERE ${where.sqlStr}" else ""
|
||||
}${if (order != null) " ORDER BY ${order.fieldName} ${if (reverse) "DESC" else "ASC"}" else ""
|
||||
}${if (maxCount != null) " limit $maxCount" else ""
|
||||
};"
|
||||
return suspendCoroutine { cont ->
|
||||
connection.query(sql) {
|
||||
if (it.succeeded()) {
|
||||
adapter.adapt(it.result())
|
||||
cont.resume(adapter)
|
||||
} else {
|
||||
cont.resumeWithException(it.cause())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun <T : Any> select(
|
||||
adapter: AsyncSqlAdapter<T>,
|
||||
fields: String,
|
||||
where: String?,
|
||||
order: String?,
|
||||
reverse: Boolean,
|
||||
maxCount: Int?,
|
||||
table: String
|
||||
): AsyncSqlAdapter<T> {
|
||||
val sql = "SELECT $fields FROM ${adapter.clazz.tableName
|
||||
}${if (where != null) " WHERE $where" else ""
|
||||
}${if (order != null) " ORDER BY $order ${if (reverse) "DESC" else "ASC"}" else ""
|
||||
}${if (maxCount != null) " limit $maxCount" else ""
|
||||
};"
|
||||
return suspendCoroutine { cont ->
|
||||
connection.query(sql) {
|
||||
if (it.succeeded()) {
|
||||
adapter.adapt(it.result())
|
||||
cont.resume(adapter)
|
||||
} else {
|
||||
cont.resumeWithException(it.cause())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据库数据
|
||||
* @param value 用来存储数据的bean对象
|
||||
* @param where SQL语句的一部分,用来限定查找的条件。每一条String储存一个条件
|
||||
*/
|
||||
override suspend fun update(value: Any, where: Clause): Int {
|
||||
val sb = StringBuilder()
|
||||
value.javaClass.declaredFields.forEach {
|
||||
it.isAccessible = true
|
||||
sb.append("${it.fieldName}=${it.get(value)?.fieldValue ?: return@forEach},")
|
||||
}
|
||||
if (sb.isNotEmpty())
|
||||
sb.delete(sb.length - 1, sb.length)
|
||||
val whereStr = where.sqlStr
|
||||
val sql = "UPDATE ${value.tableName} SET $sb${if (whereStr.isNotEmpty()) " WHERE ${whereStr}" else ""};"
|
||||
return doSql(sql)
|
||||
}
|
||||
|
||||
override suspend fun update(table: String, set: String, where: String?): Int {
|
||||
val sql = "UPDATE $table SET $set${if (where != null && where.isNotEmpty()) " WHERE $where" else ""};"
|
||||
return doSql(sql)
|
||||
}
|
||||
|
||||
private suspend fun insert(sql: String, table: Class<*>): Int {
|
||||
return try {
|
||||
doSql(sql)
|
||||
} catch (e: SQLSyntaxErrorException) {
|
||||
createTable(table)
|
||||
doSql(sql)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insert(value: Any): Int {
|
||||
val clazz = value.javaClass
|
||||
val fields = clazz.declaredFields
|
||||
val sql = "INSERT INTO ${value.tableName} (${fields.fieldStr()}) VALUES (${clazz.valueStr(value) ?: return 0});"
|
||||
return insert(sql, clazz)
|
||||
}
|
||||
|
||||
override suspend fun insert(valueList: Iterable<*>): Int {
|
||||
val first = valueList.firstOrNull() ?: return 0
|
||||
val clazz = first.javaClass
|
||||
val fields = ArrayList<SqlFieldData>()
|
||||
clazz.declaredFields.forEach { field ->
|
||||
if (field.ignored) return@forEach
|
||||
val getter = field.getAnnotation(Getter::class.java)?.let { clazz.getDeclaredMethod(it.getter) }
|
||||
fields.add(SqlFieldData(field, getter))
|
||||
}
|
||||
val values = fields.valueStr(valueList) ?: return 0
|
||||
if (values.isEmpty()) return 0
|
||||
val sql = "INSERT INTO ${first.tableName} (${first.javaClass.declaredFields.fieldStr()}) VALUES $values;"
|
||||
return insert(sql, clazz)
|
||||
}
|
||||
|
||||
override suspend fun delete(table: String, where: String?): Int {
|
||||
val sql = "DELETE FROM `$table`${if (where != null) " WHERE $where" else ""};"
|
||||
return doSql(sql)
|
||||
|
||||
}
|
||||
|
||||
override suspend fun delete(table: String, where: Clause?) =
|
||||
delete(table, where?.sqlStr)
|
||||
|
||||
override suspend fun close() {
|
||||
connection.close()
|
||||
}
|
||||
|
||||
companion object {
|
||||
init {
|
||||
Class.forName("com.mysql.cj.jdbc.Driver")
|
||||
}
|
||||
|
||||
fun createTableStr(keys: Class<*>, engine: String = "InnoDB", charset: String = "utf8"): String =
|
||||
createTableStr(keys.tableName, keys, engine, charset)
|
||||
|
||||
fun createTableStr(table: String, keys: Class<*>, engine: String = "InnoDB", charset: String = "utf8"): String {
|
||||
val fieldSet = keys.declaredFields
|
||||
val valueStrBuilder = StringBuilder()
|
||||
valueStrBuilder.append("CREATE TABLE IF NOT EXISTS `$table`(")
|
||||
val primaryKeySet = ArrayList<String>()
|
||||
|
||||
val foreignKey = keys.getAnnotation(ForeignKey::class.java)?.let {
|
||||
if (it.target.isNotEmpty()) it.target else null
|
||||
}
|
||||
val foreignKeyList = ArrayList<Pair<String, String>>()
|
||||
|
||||
fieldSet.forEach {
|
||||
valueStrBuilder.appendField(it, { it.fieldType }, foreignKeyList) {
|
||||
primaryKeySet.add(fieldName)
|
||||
}
|
||||
}
|
||||
|
||||
if (primaryKeySet.isNotEmpty()) {
|
||||
valueStrBuilder.append("PRIMARY KEY(${primaryKeySet.fieldName}),")
|
||||
}
|
||||
|
||||
if (foreignKey != null && foreignKeyList.isEmpty()) {
|
||||
val (source, target) = foreignKeyList.fieldStr()
|
||||
valueStrBuilder.append("FOREIGN KEY ($source) REFERENCES $foreignKey ($target),")
|
||||
}
|
||||
valueStrBuilder.deleteCharAt(valueStrBuilder.length - 1)
|
||||
|
||||
valueStrBuilder.append(")ENGINE=$engine DEFAULT CHARSET=$charset;")
|
||||
return valueStrBuilder.toString()
|
||||
}
|
||||
|
||||
private val Field.fieldType: String?
|
||||
get() = getAnnotation(FieldType::class.java)?.name ?: when (type) {
|
||||
java.lang.Byte::class.java -> "TINYINT"
|
||||
java.lang.Character::class.java -> "TINYINT"
|
||||
java.lang.Short::class.java -> "SMALLINT"
|
||||
java.lang.Integer::class.java -> "INT"
|
||||
java.lang.Long::class.java -> "BIGINT"
|
||||
java.lang.Float::class.java -> "FLOAT"
|
||||
java.lang.Double::class.java -> "DOUBLE"
|
||||
|
||||
Byte::class.java -> "TINYINT"
|
||||
Char::class.java -> "TINYINT"
|
||||
Short::class.java -> "SMALLINT"
|
||||
Int::class.java -> "INT"
|
||||
Long::class.java -> "BIGINT"
|
||||
Float::class.java -> "FLOAT"
|
||||
Double::class.java -> "Double"
|
||||
|
||||
java.lang.String::class.java -> getAnnotation(TextLength::class.java)?.let { "CHAR(${it.length})" }
|
||||
?: "TEXT"
|
||||
else ->
|
||||
getAnnotation<FieldType>()?.name ?: type.getAnnotation<FieldType>()?.name ?: type.name.split('.').last()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,307 +0,0 @@
|
||||
package cn.tursom.database.async.sqlite
|
||||
|
||||
import cn.tursom.core.simplifyPath
|
||||
import cn.tursom.database.SqlFieldData
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.SqlUtils.fieldStr
|
||||
import cn.tursom.database.SqlUtils.fieldValue
|
||||
import cn.tursom.database.SqlUtils.getAnnotation
|
||||
import cn.tursom.database.SqlUtils.ignored
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import cn.tursom.database.SqlUtils.valueStr
|
||||
import cn.tursom.database.annotation.*
|
||||
import cn.tursom.database.async.AsyncSqlAdapter
|
||||
import cn.tursom.database.async.AsyncSqlHelper
|
||||
import cn.tursom.database.async.vertx
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import io.vertx.core.json.JsonObject
|
||||
import io.vertx.ext.jdbc.JDBCClient
|
||||
import io.vertx.ext.sql.SQLConnection
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.sqlite.SQLiteException
|
||||
import java.io.File
|
||||
import java.lang.reflect.Field
|
||||
import java.sql.Connection
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class AsyncSqliteHelper(base: String) : AsyncSqlHelper {
|
||||
private val path = File(base).absolutePath.simplifyPath()
|
||||
|
||||
override val connection = runBlocking {
|
||||
val config = JsonObject()
|
||||
config.put("url", "jdbc:sqlite:$path")
|
||||
config.put("driver_class", "org.sqlite.JDBC")
|
||||
suspendCoroutine<SQLConnection> { cont ->
|
||||
JDBCClient.createShared(vertx, config, File(base).absolutePath.replace(".${File.separator}", "")).getConnection {
|
||||
if (!it.failed()) {
|
||||
val conn = it.result()
|
||||
|
||||
// 我们要利用反射强行把我们需要的方法注入进去
|
||||
val connField = conn.javaClass.getDeclaredField("conn")
|
||||
connField.isAccessible = true
|
||||
val fieldConn = connField.get(conn)
|
||||
val innerField = fieldConn.javaClass.getDeclaredField("inner")
|
||||
innerField.isAccessible = true
|
||||
org.sqlite.Function.create(innerField.get(fieldConn) as Connection, "REGEXP", regexp)
|
||||
|
||||
cont.resume(conn)
|
||||
} else {
|
||||
cont.resumeWithException(it.cause())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun doSql(sql: String): Int = suspendCoroutine { cont ->
|
||||
connection.execute(sql) {
|
||||
if (it.succeeded()) {
|
||||
cont.resume(1)
|
||||
} else {
|
||||
cont.resumeWithException(it.cause())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun commit() = suspendCoroutine<Unit> { cont -> connection.commit { cont.resume(Unit) } }
|
||||
|
||||
suspend fun createTable(fields: Class<*>, table: String) {
|
||||
val sql = createTableStr(fields, table)
|
||||
doSql(sql)
|
||||
}
|
||||
|
||||
override suspend fun createTable(fields: Class<*>) {
|
||||
createTable(fields, fields.tableName)
|
||||
}
|
||||
|
||||
override suspend fun deleteTable(table: String) {
|
||||
val sql = "DROP TABLE if exists $table"
|
||||
doSql(sql)
|
||||
}
|
||||
|
||||
override suspend fun dropTable(table: String) {
|
||||
deleteTable(table)
|
||||
}
|
||||
|
||||
override suspend fun <T : Any> select(
|
||||
adapter: AsyncSqlAdapter<T>,
|
||||
fields: Iterable<String>,
|
||||
where: Clause?,
|
||||
order: Field?,
|
||||
reverse: Boolean,
|
||||
maxCount: Int?,
|
||||
table: String
|
||||
): AsyncSqlAdapter<T> =
|
||||
select(adapter, fields.fieldStr(), where?.sqlStr, order?.fieldName, reverse, maxCount)
|
||||
|
||||
override suspend fun <T : Any> select(
|
||||
adapter: AsyncSqlAdapter<T>,
|
||||
fields: String,
|
||||
where: String?,
|
||||
order: String?,
|
||||
reverse: Boolean,
|
||||
maxCount: Int?,
|
||||
table: String
|
||||
): AsyncSqlAdapter<T> {
|
||||
val sql = "SELECT $fields FROM ${table
|
||||
}${if (where != null) " WHERE $where" else ""
|
||||
}${if (order != null) " ORDER BY $order ${if (reverse) "DESC" else "ASC"}" else ""
|
||||
}${if (maxCount != null) " limit $maxCount" else ""
|
||||
};"
|
||||
return try {
|
||||
suspendCoroutine { cont ->
|
||||
connection.query(sql) {
|
||||
if (it.succeeded()) {
|
||||
adapter.adapt(it.result()!!)
|
||||
cont.resume(adapter)
|
||||
} else {
|
||||
cont.resumeWithException(it.cause())
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: SQLiteException) {
|
||||
if (e.message != "[SQLITE_ERROR] SQL error or missing cn.tusom.database (no such table: ${adapter.clazz.tableName})") throw e
|
||||
createTable(adapter.clazz)
|
||||
adapter
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun insert(sql: String, table: Class<*>, tableName: String): Int {
|
||||
@Suppress("DuplicatedCode")
|
||||
return try {
|
||||
doSql(sql)
|
||||
} catch (e: Throwable) {
|
||||
createTable(table, tableName)
|
||||
doSql(sql)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insert(value: Any): Int {
|
||||
val clazz = value.javaClass
|
||||
val fields = clazz.declaredFields
|
||||
val column = fields.fieldStr()
|
||||
val valueStr = fields.valueStr(value) ?: return 0
|
||||
val sql = "INSERT INTO ${value.tableName} ($column) VALUES ($valueStr);"
|
||||
return insert(sql, clazz, clazz.tableName)
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
override suspend fun insert(valueList: Iterable<*>): Int {
|
||||
val first = valueList.firstOrNull() ?: return 0
|
||||
val clazz = first.javaClass
|
||||
val fields = ArrayList<SqlFieldData>()
|
||||
clazz.declaredFields.forEach { field ->
|
||||
if (field.ignored) return@forEach
|
||||
val getter = field.getAnnotation(Getter::class.java)?.let { clazz.getDeclaredMethod(it.getter) }
|
||||
fields.add(SqlFieldData(field, getter))
|
||||
}
|
||||
val values = fields.valueStr(valueList) ?: return 0
|
||||
if (values.isEmpty()) return 0
|
||||
val sql = "INSERT INTO ${first.tableName} (${clazz.declaredFields.fieldStr()}) VALUES $values;"
|
||||
return insert(sql, clazz, clazz.tableName)
|
||||
}
|
||||
|
||||
override suspend fun replace(table: String, value: Any): Int {
|
||||
val clazz = value.javaClass
|
||||
val fields = clazz.declaredFields
|
||||
val column = fields.fieldStr()
|
||||
val valueStr = fields.valueStr(value) ?: return 0
|
||||
val sql = "REPLACE INTO $table ($column) VALUES ($valueStr);"
|
||||
return insert(sql, clazz, table)
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
override suspend fun replace(table: String, valueList: Iterable<*>): Int {
|
||||
val first = valueList.firstOrNull() ?: return 0
|
||||
val clazz = first.javaClass
|
||||
val fields = ArrayList<SqlFieldData>()
|
||||
clazz.declaredFields.forEach { field ->
|
||||
if (field.ignored) return@forEach
|
||||
val getter = field.getAnnotation(Getter::class.java)?.let { clazz.getDeclaredMethod(it.getter) }
|
||||
fields.add(SqlFieldData(field, getter))
|
||||
}
|
||||
val values = fields.valueStr(valueList) ?: return 0
|
||||
if (values.isEmpty()) return 0
|
||||
val sql = "REPLACE INTO $table (${clazz.declaredFields.fieldStr()}) VALUES $values;"
|
||||
return insert(sql, clazz, table)
|
||||
}
|
||||
|
||||
override suspend fun update(
|
||||
value: Any, where: Clause
|
||||
): Int {
|
||||
val set = StringBuilder()
|
||||
value.javaClass.declaredFields.forEach {
|
||||
it.isAccessible = true
|
||||
it.get(value)?.let { value ->
|
||||
set.append("${it.fieldName}=${value.fieldValue},")
|
||||
}
|
||||
}
|
||||
if (set.isNotEmpty()) {
|
||||
set.delete(set.length - 1, set.length)
|
||||
}
|
||||
val sql = "UPDATE ${value.tableName} SET $set WHERE ${where.sqlStr};"
|
||||
return doSql(sql)
|
||||
}
|
||||
|
||||
override suspend fun update(table: String, set: String, where: String?): Int {
|
||||
val sql = "UPDATE $table SET $set${if (where != null && where.isNotEmpty()) " WHERE $where" else ""};"
|
||||
return doSql(sql)
|
||||
}
|
||||
|
||||
override suspend fun delete(table: String, where: String?): Int {
|
||||
val sql = "DELETE FROM $table${if (where?.isNotEmpty() == true) " WHERE $where" else ""};"
|
||||
return doSql(sql)
|
||||
}
|
||||
|
||||
override suspend fun delete(table: String, where: Clause?): Int {
|
||||
return delete(table, where?.sqlStr)
|
||||
}
|
||||
|
||||
override suspend fun close() {
|
||||
connection.close()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val Field.fieldType: String?
|
||||
get() = when (type) {
|
||||
java.lang.Byte::class.java -> "INTEGER"
|
||||
java.lang.Short::class.java -> "INTEGER"
|
||||
java.lang.Integer::class.java -> "INTEGER"
|
||||
java.lang.Long::class.java -> "INTEGER"
|
||||
java.lang.Float::class.java -> "REAL"
|
||||
java.lang.Double::class.java -> "REAL"
|
||||
|
||||
Byte::class.java -> "INTEGER"
|
||||
Char::class.java -> "INTEGER"
|
||||
Short::class.java -> "INTEGER"
|
||||
Int::class.java -> "INTEGER"
|
||||
Long::class.java -> "INTEGER"
|
||||
Float::class.java -> "REAL"
|
||||
Double::class.java -> "REAL"
|
||||
|
||||
java.lang.String::class.java -> getAnnotation<TextLength>()?.let { "CHAR(${it.length})" } ?: "TEXT"
|
||||
|
||||
else -> {
|
||||
getAnnotation<FieldType>()?.name ?: type.getAnnotation<FieldType>()?.name ?: type.name.split('.').last()
|
||||
}
|
||||
}
|
||||
|
||||
private val regexp = object : org.sqlite.Function() {
|
||||
override fun xFunc() {
|
||||
val regex = Regex(value_text(0) ?: "")
|
||||
val value = value_text(1) ?: ""
|
||||
result(if (regex.containsMatchIn(value)) 1 else 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendField(
|
||||
field: Field,
|
||||
foreignKeyList: java.util.AbstractCollection<in Pair<String, String>>
|
||||
) {
|
||||
if (field.ignored) return
|
||||
val fieldName = field.fieldName
|
||||
append("`$fieldName` ${field.fieldType ?: return}")
|
||||
field.annotations.forEach annotations@{ annotation ->
|
||||
append(" ${when (annotation) {
|
||||
is NotNull -> "NOT NULL"
|
||||
is Unique -> "UNIQUE"
|
||||
is Default -> "DEFAULT ${annotation.default}"
|
||||
is Check -> "CHECK(${field.fieldName}${annotation.func})"
|
||||
is ExtraAttribute -> annotation.attributes
|
||||
is PrimaryKey -> " PRIMARY KEY${field.getAnnotation(AutoIncrement::class.java)?.let { " AUTOINCREMENT" }
|
||||
?: ""}"
|
||||
is ForeignKey -> {
|
||||
foreignKeyList.add(fieldName to if (annotation.target.isNotEmpty()) annotation.target else fieldName)
|
||||
return@annotations
|
||||
}
|
||||
else -> return@annotations
|
||||
}}")
|
||||
}
|
||||
append(',')
|
||||
}
|
||||
|
||||
fun createTableStr(keys: Class<*>, table: String): String {
|
||||
val foreignKey = keys.getAnnotation(ForeignKey::class.java)?.let {
|
||||
if (it.target.isNotEmpty()) it.target else null
|
||||
}
|
||||
val foreignKeyList = ArrayList<Pair<String, String>>()
|
||||
|
||||
val valueStrBuilder = StringBuilder()
|
||||
valueStrBuilder.append("CREATE TABLE IF NOT EXISTS $table(")
|
||||
keys.declaredFields.forEach {
|
||||
valueStrBuilder.appendField(it, foreignKeyList)
|
||||
}
|
||||
|
||||
foreignKey?.let {
|
||||
if (foreignKeyList.isEmpty()) return@let
|
||||
val (source, target) = foreignKeyList.fieldStr()
|
||||
valueStrBuilder.append("FOREIGN KEY ($source) REFERENCES $it ($target),")
|
||||
}
|
||||
|
||||
valueStrBuilder.deleteCharAt(valueStrBuilder.length - 1)
|
||||
valueStrBuilder.append(");")
|
||||
return valueStrBuilder.toString()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.async
|
||||
|
||||
import io.vertx.core.Vertx
|
||||
|
||||
val vertx = Vertx.vertx()
|
@ -1,61 +0,0 @@
|
||||
package cn.tursom.utils.cache
|
||||
|
||||
import cn.tursom.database.async.AsyncSqlAdapter
|
||||
import cn.tursom.database.async.AsyncSqlHelper
|
||||
import cn.tursom.database.clauses.clause
|
||||
import cn.tursom.utils.background
|
||||
import cn.tursom.utils.cache.interfaces.AsyncPotableCacheMap
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
|
||||
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate", "SpellCheckingInspection")
|
||||
class AsyncSqlStringCacheMap(
|
||||
val db: AsyncSqlHelper,
|
||||
val timeout: Long,
|
||||
val table: String,
|
||||
val updateDelay: Long = 60 * 1000,
|
||||
val prevCacheMap: AsyncPotableCacheMap<String, String> = DefaultAsyncPotableCacheMap(timeout),
|
||||
val logger: Logger? = null
|
||||
) : AsyncPotableCacheMap<String, String> by prevCacheMap {
|
||||
|
||||
override suspend fun get(key: String): String? {
|
||||
return prevCacheMap.get(key) ?: try {
|
||||
val storage = db.select(AsyncSqlAdapter(StorageData::class.java), where = clause { !StorageData::key equal !key }, maxCount = 1, table = table)
|
||||
if (storage.isNotEmpty() && storage[0].cacheTime + timeout > System.currentTimeMillis()) {
|
||||
val value = storage[0].value
|
||||
set(key, value)
|
||||
value
|
||||
} else null
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun get(key: String, constructor: suspend () -> String): String {
|
||||
val memCache = get(key)
|
||||
return if (memCache != null) memCache
|
||||
else {
|
||||
val newValue = constructor()
|
||||
this.set(key, newValue)
|
||||
newValue
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun set(key: String, value: String): String? {
|
||||
prevCacheMap.set(key, value)
|
||||
background { updateStorage(key, value) }
|
||||
return value
|
||||
}
|
||||
|
||||
suspend fun updateStorage(key: String, value: String) {
|
||||
try {
|
||||
val updated = db.replace(table, StorageData(key, value))
|
||||
logger?.log(Level.INFO, "AsyncSqlStringCacheMap update $updated coloums: $key")
|
||||
} catch (e: Exception) {
|
||||
System.err.println("AsyncSqlStringCacheMap cause an Exception on update cn.tusom.database")
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
8
database/database-mysql/build.gradle
Normal file
8
database/database-mysql/build.gradle
Normal file
@ -0,0 +1,8 @@
|
||||
dependencies {
|
||||
implementation project(":")
|
||||
implementation project(":database")
|
||||
// cn.tusom.database.mysql 依赖的库
|
||||
implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.19'
|
||||
// 数据库与序列化部分可选
|
||||
api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.database.wrapper.MysqlWrapper
|
||||
import java.io.Serializable
|
||||
|
||||
class MysqlSqlHelper<T> : SqlHelper<T, MysqlWrapper<T>> {
|
||||
override fun save(entity: T): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun saveBatch(entityList: Collection<T>, batchSize: Int): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun saveOrUpdateBatch(entityList: Collection<T>, batchSize: Int): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun removeById(id: Serializable?): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun removeByMap(columnMap: Map<String, Any?>): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun remove(queryWrapper: MysqlWrapper<T>): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun removeByIds(idList: Collection<Serializable>): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun updateById(entity: T): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun update(entity: T?, updateWrapper: MysqlWrapper<T>): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun updateBatchById(entityList: Collection<T>, batchSize: Int): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun saveOrUpdate(entity: T?): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getById(id: Serializable?): T? {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun listByIds(idList: Collection<Serializable>): Collection<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun listByMap(columnMap: Map<String, Any?>): Collection<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getOne(queryWrapper: MysqlWrapper<T>, throwEx: Boolean): T? {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getMap(queryWrapper: MysqlWrapper<T>): Map<String, Any?> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun count(queryWrapper: MysqlWrapper<T>): Int {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun list(queryWrapper: MysqlWrapper<T>): List<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getBaseMapper(): Mapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
package cn.tursom.database.wrapper
|
||||
|
||||
import java.util.function.BiPredicate
|
||||
import java.util.function.Consumer
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
open class MysqlWrapper<T> : AbstractWrapper<T, MysqlWrapper<T>> {
|
||||
override fun <V> allEq(condition: Boolean, params: Map<KProperty1<T, *>, V>?, null2IsNull: Boolean): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun <V> allEq(condition: Boolean, filter: BiPredicate<KProperty1<T, *>, V>?, params: Map<KProperty1<T, *>, V>?, null2IsNull: Boolean): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun eq(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun ne(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun gt(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun ge(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun lt(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun le(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun between(condition: Boolean, column: KProperty1<T, *>, val1: Any?, val2: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun notBetween(condition: Boolean, column: KProperty1<T, *>, val1: Any?, val2: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun like(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun notLike(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun likeLeft(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun likeRight(condition: Boolean, column: KProperty1<T, *>, `val`: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun and(condition: Boolean, consumer: Consumer<MysqlWrapper<T>>?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun or(condition: Boolean, consumer: Consumer<MysqlWrapper<T>>?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun or(condition: Boolean): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun nested(condition: Boolean, consumer: Consumer<MysqlWrapper<T>>?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun apply(condition: Boolean, applySql: String?, vararg value: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun last(condition: Boolean, lastSql: String?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun comment(condition: Boolean, comment: String?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun exists(condition: Boolean, existsSql: String?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun notExists(condition: Boolean, notExistsSql: String?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun isNull(condition: Boolean, column: KProperty1<T, *>): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun isNotNull(condition: Boolean, column: KProperty1<T, *>): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun `in`(condition: Boolean, column: KProperty1<T, *>, coll: Collection<*>?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun notIn(condition: Boolean, column: KProperty1<T, *>, coll: Collection<*>?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun inSql(condition: Boolean, column: KProperty1<T, *>, inValue: String?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun notInSql(condition: Boolean, column: KProperty1<T, *>, inValue: String?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun groupBy(condition: Boolean, vararg columns: KProperty1<T, *>): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun orderBy(condition: Boolean, isAsc: Boolean, vararg columns: KProperty1<T, *>): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun having(condition: Boolean, sqlHaving: String?, vararg params: Any?): MysqlWrapper<T> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
@ -1,264 +0,0 @@
|
||||
@file:Suppress("SqlDialectInspection", "SqlNoDataSourceInspection")
|
||||
|
||||
package cn.tursom.database
|
||||
|
||||
import java.sql.Connection
|
||||
import java.sql.DriverManager
|
||||
import java.sql.SQLException
|
||||
import java.util.*
|
||||
|
||||
//////////////////////////////// 数据库连接池类 ConnectionPool.java ////////////////////////////////////////
|
||||
|
||||
/*
|
||||
这个例子是根据POSTGRESQL数据库写的,
|
||||
请用的时候根据实际的数据库调整。
|
||||
调用方法如下:
|
||||
①
|
||||
ConnectionPool connPool
|
||||
= new ConnectionPool("com.microsoft.jdbc.sqlserver.SQLServerDriver"
|
||||
,"jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=MyDataForTest"
|
||||
,"Username"
|
||||
,"Password");
|
||||
②
|
||||
Connection conn = connPool .getConnection();
|
||||
connPool.returnConnection(conn);
|
||||
connPool.refreshConnections();
|
||||
connPool.closeConnectionPool();
|
||||
*/
|
||||
|
||||
class ConnectionPool
|
||||
// 它中存放的对象为 PooledConnection 型
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param jdbcDriver
|
||||
* String JDBC 驱动类串
|
||||
* @param dbUrl
|
||||
* String 数据库 URL
|
||||
* @param dbUsername
|
||||
* String 连接数据库用户名
|
||||
* @param dbPassword
|
||||
* String 连接数据库用户的密码
|
||||
*/
|
||||
(
|
||||
private val jdbcDriver: String = "",// 数据库驱动
|
||||
private val dbUrl: String = "",// 数据库驱动
|
||||
private val dbUsername: String = "",// 数据库用户名
|
||||
private val dbPassword: String = ""// 数据库用户密码
|
||||
) {
|
||||
|
||||
/**
|
||||
* 测试表的名字
|
||||
*/
|
||||
var testTable = "" // 测试连接是否可用的测试表名,默认没有测试表
|
||||
|
||||
/**
|
||||
* 连接池中最大可用的连接数量
|
||||
*/
|
||||
var maxConnections = 50 // 连接池最大的大小
|
||||
|
||||
private var connections = Vector<PooledConnection>(0) // 存放连接池中数据库连接的向量 , 初始时为 null
|
||||
|
||||
/**
|
||||
* 通过调用 getFreeConnection() 函数返回一个可用的数据库连接 , 如果当前没有可用的数据库连接,并且更多的数据库连接不能创
|
||||
* 建(如连接池大小的限制),此函数等待一会再尝试获取。
|
||||
*
|
||||
* @return 返回一个可用的数据库连接对象
|
||||
*/
|
||||
val connection: Connection?
|
||||
@Synchronized @Throws(SQLException::class)
|
||||
get() = findFreeConnection()
|
||||
|
||||
|
||||
/**
|
||||
* 创建一个新的数据库连接并返回它
|
||||
*
|
||||
* @return 返回一个新创建的数据库连接
|
||||
*/
|
||||
private fun newConnection(): Connection {
|
||||
// 创建一个数据库连接
|
||||
val conn = DriverManager.getConnection(dbUrl, dbUsername,
|
||||
dbPassword)
|
||||
// 如果这是第一次创建数据库连接,即检查数据库,获得此数据库允许支持的
|
||||
// 最大客户连接数目
|
||||
// connections.size()==0 表示目前没有连接己被创建
|
||||
if (connections.size == 0) {
|
||||
val metaData = conn.metaData
|
||||
val driverMaxConnections = metaData.maxConnections
|
||||
// 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大
|
||||
// 连接限制,或数据库的最大连接限制不知道
|
||||
// driverMaxConnections 为返回的一个整数,表示此数据库允许客户连接的数目
|
||||
// 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大
|
||||
// 连接数目为数据库允许的最大数目
|
||||
if (driverMaxConnections > 0 && this.maxConnections > driverMaxConnections) {
|
||||
this.maxConnections = driverMaxConnections
|
||||
}
|
||||
}
|
||||
return conn // 返回创建的新的数据库连接
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找连接池中所有的连接,查找一个可用的数据库连接, 如果没有可用的连接,返回 null
|
||||
*
|
||||
* @return 返回一个可用的数据库连接
|
||||
*/
|
||||
private fun findFreeConnection(): Connection? {
|
||||
var conn: Connection?
|
||||
connections.forEach {
|
||||
if (!it.isBusy) {
|
||||
// 如果此对象不忙,则获得它的数据库连接并把它设为忙
|
||||
conn = it.connection
|
||||
it.isBusy = true
|
||||
// 测试此连接是否可用
|
||||
if (!testConnection(conn)) {
|
||||
// 如果此连接不可再用了,则创建一个新的连接,
|
||||
// 并替换此不可用的连接对象,如果创建失败,返回 null
|
||||
try {
|
||||
conn = newConnection()
|
||||
} catch (e: SQLException) {
|
||||
System.err.println(" 创建数据库连接失败! " + e.message)
|
||||
return null
|
||||
}
|
||||
it.connection = conn
|
||||
}
|
||||
return conn// 己经找到一个可用的连接,退出
|
||||
}
|
||||
}
|
||||
conn = newConnection()
|
||||
return conn// 返回找到到的可用连接
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试一个连接是否可用,如果不可用,关掉它并返回 false 否则可用返回 true
|
||||
*
|
||||
* @param conn 需要测试的数据库连接
|
||||
* @return 返回 true 表示此连接可用, false 表示不可用
|
||||
*/
|
||||
private fun testConnection(conn: Connection?): Boolean {
|
||||
try {
|
||||
// 判断测试表是否存在
|
||||
if (testTable == "") {
|
||||
// 如果测试表为空,试着使用此连接的 setAutoCommit() 方法
|
||||
// 来判断连接否可用(此方法只在部分数据库可用,如果不可用 ,
|
||||
// 抛出异常)。注意:使用测试表的方法更可靠
|
||||
conn!!.autoCommit = true
|
||||
} else {// 有测试表的时候使用测试表测试
|
||||
// check if this connection is valid
|
||||
val stmt = conn!!.createStatement()
|
||||
stmt.execute("select count(*) from $testTable")
|
||||
}
|
||||
} catch (e: SQLException) {
|
||||
// 上面抛出异常,此连接己不可用,关闭它,并返回 false;
|
||||
closeConnection(conn!!)
|
||||
return false
|
||||
}
|
||||
|
||||
// 连接可用,返回 true
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 此函数返回一个数据库连接到连接池中,并把此连接置为空闲。 所有使用连接池获得的数据库连接均应在不使用此连接时返回它。
|
||||
*
|
||||
* @param conn 需返回到连接池中的连接对象
|
||||
*/
|
||||
|
||||
fun returnConnection(conn: Connection) {
|
||||
// 遍历连接池中的所有连接,找到这个要返回的连接对象
|
||||
connections.forEach {
|
||||
// 先找到连接池中的要返回的连接对象
|
||||
if (conn === it.connection) {
|
||||
// 找到了 , 设置此连接为空闲状态
|
||||
it.isBusy = false
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新连接池中所有的连接对象
|
||||
*
|
||||
*/
|
||||
@Synchronized
|
||||
@Throws(SQLException::class)
|
||||
fun refreshConnections() {
|
||||
connections.forEach {
|
||||
// 如果对象忙则等 5 秒 ,5 秒后直接刷新
|
||||
if (it.isBusy) {
|
||||
wait(5000) // 等 5 秒
|
||||
}
|
||||
// 关闭此连接,用一个新的连接代替它。
|
||||
closeConnection(it.connection!!)
|
||||
it.connection = newConnection()
|
||||
it.isBusy = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭连接池中所有的连接,并清空连接池。
|
||||
*/
|
||||
@Synchronized
|
||||
@Throws(SQLException::class)
|
||||
fun closeConnectionPool() {
|
||||
var pConn: PooledConnection
|
||||
val enumerate = connections.elements()
|
||||
while (enumerate.hasMoreElements()) {
|
||||
pConn = enumerate.nextElement() as PooledConnection
|
||||
// 如果忙,等 5 秒
|
||||
if (pConn.isBusy) {
|
||||
wait(5000) // 等 5 秒
|
||||
}
|
||||
// 5 秒后直接关闭它
|
||||
closeConnection(pConn.connection!!)
|
||||
// 从连接池向量中删除它
|
||||
connections.removeElement(pConn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭一个数据库连接
|
||||
*
|
||||
* @param conn 需要关闭的数据库连接
|
||||
*/
|
||||
private fun closeConnection(conn: Connection) {
|
||||
try {
|
||||
conn.close()
|
||||
} catch (e: SQLException) {
|
||||
System.err.println(" 关闭数据库连接出错: " + e.message)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 使程序等待给定的毫秒数
|
||||
*
|
||||
* @param mSeconds 给定的毫秒数
|
||||
*/
|
||||
|
||||
private fun wait(mSeconds: Int) {
|
||||
try {
|
||||
Thread.sleep(mSeconds.toLong())
|
||||
} catch (e: InterruptedException) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 内部使用的用于保存连接池中连接对象的类 此类中有两个成员,一个是数据库的连接,另一个是指示此连接是否 正在使用的标志。
|
||||
*/
|
||||
|
||||
class PooledConnection// 构造函数,根据一个 Connection 构告一个 PooledConnection 对象
|
||||
(connection: Connection) {
|
||||
// 返回此对象中的连接
|
||||
// 设置此对象的,连接
|
||||
var connection: Connection? = null// 数据库连接
|
||||
// 获得对象连接是否忙
|
||||
// 设置对象的连接正在忙
|
||||
var isBusy = false // 此连接是否正在使用的标志,默认没有正在使用
|
||||
|
||||
init {
|
||||
this.connection = connection
|
||||
}
|
||||
}
|
||||
|
||||
}
|
105
database/src/main/kotlin/cn/tursom/database/Constants.kt
Normal file
105
database/src/main/kotlin/cn/tursom/database/Constants.kt
Normal file
@ -0,0 +1,105 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.database.StringPool.DOT
|
||||
|
||||
/**
|
||||
* mybatis_plus 自用常量集中管理
|
||||
*
|
||||
* @author miemie
|
||||
* @since 2018-07-22
|
||||
*/
|
||||
@Suppress("unused", "SpellCheckingInspection", "MemberVisibilityCanBePrivate")
|
||||
object Constants {
|
||||
/**
|
||||
* project name
|
||||
*/
|
||||
const val MYBATIS_PLUS = "mybatis-plus"
|
||||
/**
|
||||
* MD5
|
||||
*/
|
||||
const val MD5 = "MD5"
|
||||
/**
|
||||
* 实体类
|
||||
*/
|
||||
const val ENTITY = "et"
|
||||
/**
|
||||
* 实体类 带后缀 ==> .
|
||||
*/
|
||||
const val ENTITY_DOT = ENTITY + DOT
|
||||
/**
|
||||
* wrapper 类
|
||||
*/
|
||||
const val WRAPPER = "ew"
|
||||
/**
|
||||
* wrapper 类 带后缀 ==> .
|
||||
*/
|
||||
const val WRAPPER_DOT = WRAPPER + DOT
|
||||
/**
|
||||
* wrapper 类的属性 entity
|
||||
*/
|
||||
const val WRAPPER_ENTITY = WRAPPER_DOT + "entity"
|
||||
/**
|
||||
* wrapper 类的属性 sqlSegment
|
||||
*/
|
||||
const val WRAPPER_SQLSEGMENT = WRAPPER_DOT + "sqlSegment"
|
||||
/**
|
||||
* wrapper 类的属性 emptyOfNormal
|
||||
*/
|
||||
const val WRAPPER_EMPTYOFNORMAL = WRAPPER_DOT + "emptyOfNormal"
|
||||
/**
|
||||
* wrapper 类的属性 nonEmptyOfNormal
|
||||
*/
|
||||
const val WRAPPER_NONEMPTYOFNORMAL = WRAPPER_DOT + "nonEmptyOfNormal"
|
||||
/**
|
||||
* wrapper 类的属性 nonEmptyOfEntity
|
||||
*/
|
||||
const val WRAPPER_NONEMPTYOFENTITY = WRAPPER_DOT + "nonEmptyOfEntity"
|
||||
/**
|
||||
* wrapper 类的属性 emptyOfWhere
|
||||
*/
|
||||
const val WRAPPER_EMPTYOFWHERE = WRAPPER_DOT + "emptyOfWhere"
|
||||
/**
|
||||
* wrapper 类的判断属性 nonEmptyOfWhere
|
||||
*/
|
||||
const val WRAPPER_NONEMPTYOFWHERE = WRAPPER_DOT + "nonEmptyOfWhere"
|
||||
/**
|
||||
* wrapper 类的属性 entity 带后缀 ==> .
|
||||
*/
|
||||
const val WRAPPER_ENTITY_DOT = WRAPPER_DOT + "entity" + DOT
|
||||
/**
|
||||
* UpdateWrapper 类的属性 sqlSet
|
||||
*/
|
||||
const val U_WRAPPER_SQL_SET = WRAPPER_DOT + "sqlSet"
|
||||
/**
|
||||
* QueryWrapper 类的属性 sqlSelect
|
||||
*/
|
||||
const val Q_WRAPPER_SQL_SELECT = WRAPPER_DOT + "sqlSelect"
|
||||
/**
|
||||
* wrapper 类的属性 sqlComment
|
||||
*/
|
||||
const val Q_WRAPPER_SQL_COMMENT = WRAPPER_DOT + "sqlComment"
|
||||
/**
|
||||
* columnMap
|
||||
*/
|
||||
const val COLUMN_MAP = "cm"
|
||||
/**
|
||||
* columnMap.isEmpty
|
||||
*/
|
||||
const val COLUMN_MAP_IS_EMPTY = COLUMN_MAP + DOT.toString() + "isEmpty"
|
||||
/**
|
||||
* collection
|
||||
*/
|
||||
const val COLLECTION = "coll"
|
||||
/**
|
||||
* where
|
||||
*/
|
||||
const val WHERE = "WHERE"
|
||||
/**
|
||||
* 乐观锁字段
|
||||
*/
|
||||
const val MP_OPTLOCK_VERSION_ORIGINAL = "MP_OPTLOCK_VERSION_ORIGINAL"
|
||||
const val MP_OPTLOCK_VERSION_COLUMN = "MP_OPTLOCK_VERSION_COLUMN"
|
||||
const val MP_OPTLOCK_ET_ORIGINAL = "MP_OPTLOCK_ET_ORIGINAL"
|
||||
const val WRAPPER_PARAM = "MPGENVAL"
|
||||
const val WRAPPER_PARAM_FORMAT = "#{%s.paramNameValuePairs.%s}"
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
|
||||
class FieldSelector : HashSet<String>() {
|
||||
// operator fun String.unaryPlus() = add(this.sqlStr)
|
||||
operator fun Field.unaryPlus(): Field {
|
||||
add(fieldName)
|
||||
return this
|
||||
}
|
||||
|
||||
operator fun KProperty<*>.unaryPlus(): KProperty<*> {
|
||||
add(fieldName)
|
||||
return this
|
||||
}
|
||||
|
||||
infix operator fun Field.plus(field: Field): Field {
|
||||
add(field.fieldName)
|
||||
return this
|
||||
}
|
||||
|
||||
infix operator fun Field.plus(field: KProperty<*>): Field {
|
||||
add(field.fieldName)
|
||||
return this
|
||||
}
|
||||
|
||||
infix operator fun KProperty<*>.plus(field: KProperty<*>): KProperty<*> {
|
||||
add(field.fieldName)
|
||||
return this
|
||||
}
|
||||
|
||||
infix operator fun KProperty<*>.plus(field: Field): KProperty<*> {
|
||||
add(field.fieldName)
|
||||
return this
|
||||
}
|
||||
}
|
123
database/src/main/kotlin/cn/tursom/database/Mapper.kt
Normal file
123
database/src/main/kotlin/cn/tursom/database/Mapper.kt
Normal file
@ -0,0 +1,123 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.database.annotations.Param
|
||||
import cn.tursom.database.wrapper.Wrapper
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
|
||||
*
|
||||
* 这个 Mapper 支持 id 泛型
|
||||
*
|
||||
* @author hubin
|
||||
* @since 2016-01-23
|
||||
*/
|
||||
interface Mapper<T> {
|
||||
/**
|
||||
* 插入一条记录
|
||||
*
|
||||
* @param entity 实体对象
|
||||
*/
|
||||
fun insert(entity: T): Int
|
||||
|
||||
/**
|
||||
* 根据 ID 删除
|
||||
*
|
||||
* @param id 主键ID
|
||||
*/
|
||||
fun deleteById(id: Serializable): Int
|
||||
|
||||
/**
|
||||
* 根据 columnMap 条件,删除记录
|
||||
*
|
||||
* @param columnMap 表字段 map 对象
|
||||
*/
|
||||
fun deleteByMap(@Param(Constants.COLUMN_MAP) columnMap: Map<String, Any?>): Int
|
||||
|
||||
/**
|
||||
* 根据 entity 条件,删除记录
|
||||
*
|
||||
* @param wrapper 实体对象封装操作类(可以为 null)
|
||||
*/
|
||||
fun delete(@Param(Constants.WRAPPER) wrapper: Wrapper<T>): Int
|
||||
|
||||
/**
|
||||
* 删除(根据ID 批量删除)
|
||||
*
|
||||
* @param idList 主键ID列表(不能为 null 以及 empty)
|
||||
*/
|
||||
fun deleteBatchIds(@Param(Constants.COLLECTION) idList: Collection<Serializable>): Int
|
||||
|
||||
/**
|
||||
* 根据 ID 修改
|
||||
*
|
||||
* @param entity 实体对象
|
||||
*/
|
||||
fun updateById(@Param(Constants.ENTITY) entity: T): Int
|
||||
|
||||
/**
|
||||
* 根据 whereEntity 条件,更新记录
|
||||
*
|
||||
* @param entity 实体对象 (set 条件值,可以为 null)
|
||||
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
|
||||
*/
|
||||
fun update(@Param(Constants.ENTITY) entity: T, @Param(Constants.WRAPPER) updateWrapper: Wrapper<T>): Int
|
||||
|
||||
/**
|
||||
* 根据 ID 查询
|
||||
*
|
||||
* @param id 主键ID
|
||||
*/
|
||||
fun selectById(id: Serializable): T
|
||||
|
||||
/**
|
||||
* 查询(根据ID 批量查询)
|
||||
*
|
||||
* @param idList 主键ID列表(不能为 null 以及 empty)
|
||||
*/
|
||||
fun selectBatchIds(@Param(Constants.COLLECTION) idList: Collection<Serializable>): List<T>
|
||||
|
||||
/**
|
||||
* 查询(根据 columnMap 条件)
|
||||
*
|
||||
* @param columnMap 表字段 map 对象
|
||||
*/
|
||||
fun selectByMap(@Param(Constants.COLUMN_MAP) columnMap: Map<String?, Any?>?): List<T>
|
||||
|
||||
/**
|
||||
* 根据 entity 条件,查询一条记录
|
||||
*
|
||||
* @param queryWrapper 实体对象封装操作类(可以为 null)
|
||||
*/
|
||||
fun selectOne(@Param(Constants.WRAPPER) queryWrapper: Wrapper<T>): T
|
||||
|
||||
/**
|
||||
* 根据 Wrapper 条件,查询总记录数
|
||||
*
|
||||
* @param queryWrapper 实体对象封装操作类(可以为 null)
|
||||
*/
|
||||
fun selectCount(@Param(Constants.WRAPPER) queryWrapper: Wrapper<T>): Int
|
||||
|
||||
/**
|
||||
* 根据 entity 条件,查询全部记录
|
||||
*
|
||||
* @param queryWrapper 实体对象封装操作类(可以为 null)
|
||||
*/
|
||||
fun selectList(@Param(Constants.WRAPPER) queryWrapper: Wrapper<T>): List<T>
|
||||
|
||||
/**
|
||||
* 根据 Wrapper 条件,查询全部记录
|
||||
*
|
||||
* @param queryWrapper 实体对象封装操作类(可以为 null)
|
||||
*/
|
||||
fun selectMaps(@Param(Constants.WRAPPER) queryWrapper: Wrapper<T>): List<Map<String, Any?>>
|
||||
|
||||
/**
|
||||
* 根据 Wrapper 条件,查询全部记录
|
||||
*
|
||||
* 注意: 只返回第一个字段的值
|
||||
*
|
||||
* @param queryWrapper 实体对象封装操作类(可以为 null)
|
||||
*/
|
||||
fun selectObjs(@Param(Constants.WRAPPER) queryWrapper: Wrapper<T>): List<Any>
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.annotation.Constructor
|
||||
import cn.tursom.database.annotation.NotNull
|
||||
import sun.misc.Unsafe
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Method
|
||||
import java.sql.ResultSet
|
||||
import java.sql.SQLException
|
||||
import java.util.*
|
||||
import kotlin.collections.forEach
|
||||
|
||||
open class SqlAdapter<T : Any>(
|
||||
@Suppress("MemberVisibilityCanBePrivate") val clazz: Class<T>,
|
||||
private val adapter: (SqlAdapter<T>.(
|
||||
resultSet: ResultSet,
|
||||
fieldList: List<FieldData>
|
||||
) -> Unit)? = null
|
||||
) : ArrayList<T>() {
|
||||
open fun adapt(resultSet: ResultSet) {
|
||||
clear() //清空已储存的数据
|
||||
try {
|
||||
val fieldList = ArrayList<FieldData>()
|
||||
if (resultSet.next()) {
|
||||
clazz.declaredFields.forEach {
|
||||
try {
|
||||
val fieldName = it.fieldName
|
||||
resultSet.getObject(fieldName)
|
||||
it.isAccessible = true
|
||||
val constructorAnnotation = it.getAnnotation(Constructor::class.java)
|
||||
val constructor = try {
|
||||
clazz.getDeclaredMethod(constructorAnnotation.constructor, Any::class.java)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
val superConstructor = try {
|
||||
clazz.getDeclaredMethod(constructorAnnotation.constructor, ResultSet::class.java)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
constructor?.isAccessible = true
|
||||
superConstructor?.isAccessible = true
|
||||
fieldList.add(FieldData(it, fieldName, it.type, superConstructor, constructor))
|
||||
} catch (e: SQLException) {
|
||||
}
|
||||
}
|
||||
(adapter ?: SqlAdapter<T>::adaptOnce)(resultSet, fieldList)
|
||||
}
|
||||
// 遍历ResultSet
|
||||
while (resultSet.next()) {
|
||||
(adapter ?: SqlAdapter<T>::adaptOnce)(resultSet, fieldList)
|
||||
}
|
||||
} catch (e: SQLException) {
|
||||
// ignored
|
||||
} catch (e: IllegalStateException) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
open fun adaptOnce(resultSet: ResultSet, fieldList: List<FieldData>) {
|
||||
//绕过构造函数获取变量0
|
||||
val bean = unsafe.allocateInstance(clazz) as T
|
||||
fieldList.forEach { (
|
||||
field,
|
||||
fieldName,
|
||||
beanType,
|
||||
superAdapter,
|
||||
adapter
|
||||
) ->
|
||||
try {
|
||||
if (superAdapter != null) {
|
||||
//如果你有能力直接从ResultSet里面取出数据,那就随君便
|
||||
val value = try {
|
||||
superAdapter.invoke(bean, resultSet)
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.targetException
|
||||
}
|
||||
field.set(bean, value)
|
||||
} else {
|
||||
resultSet.getObject(fieldName)?.let { value ->
|
||||
//让我们把数据喂进去
|
||||
field.set(bean, handleCast(bean, field, beanType, value, adapter))
|
||||
}
|
||||
}
|
||||
} catch (e: SQLException) {
|
||||
e.printStackTrace()
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
add(bean)
|
||||
}
|
||||
|
||||
private fun handleCast(
|
||||
bean: T,
|
||||
field: Field,
|
||||
beanType: Class<*>,
|
||||
value: Any,
|
||||
adapter: Method?
|
||||
): Any? {
|
||||
val dbType = value.javaClass // 这里是获取数据库字段的类型
|
||||
return if (adapter != null) {
|
||||
try {
|
||||
adapter.invoke(bean, value)
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.targetException
|
||||
}
|
||||
} else if (beanType == java.lang.Float::class.java) {
|
||||
if (dbType == java.lang.Double::class.java) {
|
||||
(value as Double).toFloat()
|
||||
} else {
|
||||
//检查是否可以为空
|
||||
if (field.getAnnotation(NotNull::class.java) != null) {
|
||||
value.toString().toFloat()
|
||||
} else {
|
||||
value.toString().toFloatOrNull()
|
||||
}
|
||||
}
|
||||
} else if (beanType == java.lang.String::class.java && dbType != java.lang.String::class.java) {
|
||||
value.toString()
|
||||
} else if (beanType == java.lang.Boolean::class.java) {
|
||||
if (field.getAnnotation(NotNull::class.java) != null) {
|
||||
value.toString().toBoolean()
|
||||
} else {
|
||||
try {
|
||||
value.toString().toBoolean()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
data class FieldData(
|
||||
val field: Field,
|
||||
val fieldName: String,
|
||||
val beanType: Class<*>,
|
||||
val superAdapter: Method?,
|
||||
val adapter: Method?
|
||||
)
|
||||
|
||||
companion object {
|
||||
//利用Unsafe绕过构造函数获取变量
|
||||
private val unsafe by lazy {
|
||||
val field = Unsafe::class.java.getDeclaredField("theUnsafe")
|
||||
//允许通过反射设置属性的值
|
||||
field.isAccessible = true
|
||||
field.get(null) as Unsafe
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.database.clauses.ClauseMaker
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class SqlDeleter(
|
||||
private val helper: SqlHelper,
|
||||
private var table: String? = null,
|
||||
private var where: Clause? = null
|
||||
) {
|
||||
infix fun Class<*>.where(where: ClauseMaker.() -> Clause) {
|
||||
table = tableName
|
||||
this@SqlDeleter.where = ClauseMaker.where()
|
||||
}
|
||||
|
||||
infix fun KClass<*>.where(where: ClauseMaker.() -> Clause) {
|
||||
table = tableName
|
||||
this@SqlDeleter.where = ClauseMaker.where()
|
||||
}
|
||||
|
||||
fun delete() = helper.delete(table!!, where)
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package cn.tursom.database
|
||||
|
||||
interface SqlField<T> {
|
||||
fun get(): T
|
||||
val sqlValue: String
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
|
||||
data class SqlFieldData(val field: Field, val getter: Method? = null)
|
@ -1,162 +1,200 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.database.clauses.ClauseMaker
|
||||
import java.io.Closeable
|
||||
import java.lang.reflect.Field
|
||||
import cn.tursom.database.wrapper.Wrapper
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* MySQLHelper,SQLite辅助使用类
|
||||
* 实现创建表格、查询、插入和更新功能
|
||||
*/
|
||||
@Suppress("unused")
|
||||
interface SqlHelper<T, W : Wrapper<T>> {
|
||||
/**
|
||||
* 插入一条记录(选择字段,策略插入)
|
||||
*
|
||||
* @param entity 实体对象
|
||||
*/
|
||||
fun save(entity: T): Boolean
|
||||
|
||||
interface SqlHelper : Closeable {
|
||||
val closed: Boolean
|
||||
/**
|
||||
* 插入(批量)
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
*/
|
||||
fun saveBatch(entityList: Collection<T>): Boolean {
|
||||
return saveBatch(entityList, 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表格
|
||||
* @param table: 表格名
|
||||
* @param keys: 属性列表
|
||||
*/
|
||||
fun createTable(table: String, keys: Iterable<String>)
|
||||
/**
|
||||
* 插入(批量)
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
* @param batchSize 插入批次数量
|
||||
*/
|
||||
fun saveBatch(entityList: Collection<T>, batchSize: Int): Boolean
|
||||
|
||||
/**
|
||||
* 根据提供的class对象自动化创建表格
|
||||
* 但是有诸多缺陷,所以不是很建议使用
|
||||
*/
|
||||
fun createTable(fields: Class<*>)
|
||||
/**
|
||||
* 批量修改插入
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
*/
|
||||
fun saveOrUpdateBatch(entityList: Collection<T>): Boolean {
|
||||
return saveOrUpdateBatch(entityList, 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除表格
|
||||
*/
|
||||
fun deleteTable(table: String)
|
||||
/**
|
||||
* 批量修改插入
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
* @param batchSize 每次的数量
|
||||
*/
|
||||
fun saveOrUpdateBatch(entityList: Collection<T>, batchSize: Int): Boolean
|
||||
|
||||
/**
|
||||
* 删除表格
|
||||
*/
|
||||
fun dropTable(table: String)
|
||||
/**
|
||||
* 根据 ID 删除
|
||||
*
|
||||
* @param id 主键ID
|
||||
*/
|
||||
fun removeById(id: Serializable?): Boolean
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param adapter 用于保存查询结果的数据类,由SQLAdapter继承而来
|
||||
* @param fields 查询字段
|
||||
* @param where 指定从一个表或多个表中获取数据的条件,Pair左边为字段名,右边为限定的值
|
||||
* @param maxCount 最大查询数量
|
||||
*/
|
||||
fun <T : Any> select(
|
||||
adapter: SqlAdapter<T>,
|
||||
fields: Iterable<String>? = null,
|
||||
where: Clause,
|
||||
order: Field? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null
|
||||
): SqlAdapter<T>
|
||||
/**
|
||||
* 根据 columnMap 条件,删除记录
|
||||
*
|
||||
* @param columnMap 表字段 map 对象
|
||||
*/
|
||||
fun removeByMap(columnMap: Map<String, Any?>): Boolean
|
||||
|
||||
/**
|
||||
* 用于支持灵活查询
|
||||
*/
|
||||
fun <T : Any> select(
|
||||
adapter: SqlAdapter<T>,
|
||||
fields: String = "*",
|
||||
where: String? = null,
|
||||
order: String? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null
|
||||
): SqlAdapter<T>
|
||||
/**
|
||||
* 根据 entity 条件,删除记录
|
||||
*/
|
||||
fun remove(queryWrapper: W): Boolean
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param value 值
|
||||
*/
|
||||
fun insert(value: Any): Int
|
||||
/**
|
||||
* 删除(根据ID 批量删除)
|
||||
*
|
||||
* @param idList 主键ID列表
|
||||
*/
|
||||
fun removeByIds(idList: Collection<Serializable>): Boolean
|
||||
|
||||
fun insert(valueList: Iterable<*>): Int
|
||||
/**
|
||||
* 根据 ID 选择修改
|
||||
*
|
||||
* @param entity 实体对象
|
||||
*/
|
||||
fun updateById(entity: T): Boolean
|
||||
|
||||
fun insert(table: String, fields: String, values: String): Int
|
||||
/**
|
||||
* 根据 whereEntity 条件,更新记录
|
||||
*
|
||||
* @param entity 实体对象
|
||||
* @param updateWrapper 实体对象封装操作类
|
||||
*/
|
||||
fun update(entity: T?, updateWrapper: W): Boolean
|
||||
|
||||
fun update(table: String, set: String, where: String = ""): Int
|
||||
/**
|
||||
* 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
|
||||
*
|
||||
* @param updateWrapper 实体对象封装操作类
|
||||
*/
|
||||
fun update(updateWrapper: W): Boolean {
|
||||
return update(null, updateWrapper)
|
||||
}
|
||||
|
||||
fun update(value: Any, where: Clause): Int
|
||||
/**
|
||||
* 根据ID 批量更新
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
*/
|
||||
fun updateBatchById(entityList: Collection<T>): Boolean {
|
||||
return updateBatchById(entityList, 1000)
|
||||
}
|
||||
|
||||
fun delete(table: String, where: String? = null): Int
|
||||
/**
|
||||
* 根据ID 批量更新
|
||||
*
|
||||
* @param entityList 实体对象集合
|
||||
* @param batchSize 更新批次数量
|
||||
*/
|
||||
fun updateBatchById(entityList: Collection<T>, batchSize: Int): Boolean
|
||||
|
||||
fun delete(table: String, where: Clause?): Int
|
||||
/**
|
||||
* TableId 注解存在更新记录,否插入一条记录
|
||||
*
|
||||
* @param entity 实体对象
|
||||
*/
|
||||
fun saveOrUpdate(entity: T?): Boolean
|
||||
|
||||
fun commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于支持灵活查询
|
||||
*/
|
||||
inline fun <reified T : Any> SqlHelper.select(
|
||||
fields: String = "*",
|
||||
where: String? = null,
|
||||
order: String? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null
|
||||
): SqlAdapter<T> = select(SqlAdapter(T::class.java), fields, where, order, reverse, maxCount)
|
||||
|
||||
inline fun <reified T : Any> SqlHelper.select(
|
||||
fields: Iterable<String> = listOf("*"),
|
||||
where: Clause,
|
||||
order: Field? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null
|
||||
): SqlAdapter<T> = select(SqlAdapter(T::class.java), fields, where, order, reverse, maxCount)
|
||||
|
||||
fun <T : Any> SqlHelper.select(
|
||||
adapter: SqlAdapter<T>,
|
||||
maker: SqlSelector<T>.() -> Unit
|
||||
): SqlAdapter<T> {
|
||||
val selector = SqlSelector(this, adapter)
|
||||
selector.maker()
|
||||
return selector.select()
|
||||
}
|
||||
|
||||
inline infix fun <reified T : Any> SqlHelper.select(
|
||||
noinline maker: SqlSelector<T>.() -> Unit
|
||||
): SqlAdapter<T> = select(SqlAdapter(T::class.java), maker)
|
||||
|
||||
fun <T : Any> SqlHelper.select(
|
||||
clazz: Class<T>,
|
||||
fields: Iterable<String> = listOf("*"),
|
||||
where: Clause,
|
||||
order: Field? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null
|
||||
): SqlAdapter<T> = select(SqlAdapter(clazz), fields, where, order, reverse, maxCount)
|
||||
|
||||
/**
|
||||
* 用于支持灵活查询
|
||||
*/
|
||||
fun <T : Any> SqlHelper.select(
|
||||
clazz: Class<T>,
|
||||
fields: String = "*",
|
||||
where: String? = null,
|
||||
order: String? = null,
|
||||
reverse: Boolean = false,
|
||||
maxCount: Int? = null
|
||||
): SqlAdapter<T> = select(SqlAdapter(clazz), fields, where, order, reverse, maxCount)
|
||||
|
||||
fun SqlHelper.delete(
|
||||
clazz: Class<*>,
|
||||
where: String? = null
|
||||
) = delete(clazz.tableName, where)
|
||||
|
||||
fun SqlHelper.delete(
|
||||
table: String,
|
||||
where: ClauseMaker.() -> Clause
|
||||
) = delete(table, ClauseMaker.where())
|
||||
|
||||
infix fun SqlHelper.delete(helper: SqlDeleter.() -> Unit): Int {
|
||||
val deleter = SqlDeleter(this)
|
||||
deleter.helper()
|
||||
return deleter.delete()
|
||||
}
|
||||
|
||||
infix fun SqlHelper.update(updater: SqlUpdater.() -> Unit): Int {
|
||||
val sqlUpdater = SqlUpdater(this)
|
||||
sqlUpdater.updater()
|
||||
return sqlUpdater.update()
|
||||
/**
|
||||
* 根据 ID 查询
|
||||
*
|
||||
* @param id 主键ID
|
||||
*/
|
||||
fun getById(id: Serializable?): T?
|
||||
|
||||
/**
|
||||
* 查询(根据ID 批量查询)
|
||||
*
|
||||
* @param idList 主键ID列表
|
||||
*/
|
||||
fun listByIds(idList: Collection<Serializable>): Collection<T>
|
||||
|
||||
/**
|
||||
* 查询(根据 columnMap 条件)
|
||||
*
|
||||
* @param columnMap 表字段 map 对象
|
||||
*/
|
||||
fun listByMap(columnMap: Map<String, Any?>): Collection<T>
|
||||
|
||||
/**
|
||||
* 根据 Wrapper,查询一条记录
|
||||
*
|
||||
* @param queryWrapper 实体对象封装操作类
|
||||
* @param throwEx 有多个 result 是否抛出异常
|
||||
*/
|
||||
fun getOne(queryWrapper: W, throwEx: Boolean = true): T?
|
||||
|
||||
/**
|
||||
* 根据 Wrapper,查询一条记录
|
||||
*
|
||||
* @param queryWrapper 实体对象封装操作类
|
||||
*/
|
||||
fun getMap(queryWrapper: W): Map<String, Any?>
|
||||
|
||||
///**
|
||||
// * 根据 Wrapper,查询一条记录
|
||||
// *
|
||||
// * @param queryWrapper 实体对象封装操作类
|
||||
// * @param mapper 转换函数
|
||||
// */
|
||||
//fun <V> getObj(queryWrapper: W, mapper: Function<in Any?, V?>?): V?
|
||||
|
||||
/**
|
||||
* 根据 Wrapper 条件,查询总记录数
|
||||
*
|
||||
* @param queryWrapper 实体对象封装操作类
|
||||
*/
|
||||
fun count(queryWrapper: W): Int
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
*
|
||||
* @param queryWrapper 实体对象封装操作类
|
||||
*/
|
||||
fun list(queryWrapper: W): List<T>
|
||||
|
||||
/**
|
||||
* 获取对应 entity 的 BaseMapper
|
||||
*
|
||||
* @return BaseMapper
|
||||
*/
|
||||
fun getBaseMapper(): Mapper<T>
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
|
||||
* 此次修改主要是减少了此项业务代码的代码量(存在性验证之后的saveOrUpdate操作)
|
||||
*
|
||||
*
|
||||
* @param entity 实体对象
|
||||
*/
|
||||
fun saveOrUpdate(entity: T?, updateWrapper: W): Boolean {
|
||||
return update(entity, updateWrapper) || saveOrUpdate(entity)
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +0,0 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.SqlUtils.sqlStr
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.database.clauses.ClauseMaker
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class SqlSelector<T : Any>(
|
||||
private val helper: SqlHelper,
|
||||
private val adapter: SqlAdapter<T>
|
||||
) {
|
||||
private var where: String? = null
|
||||
private var fields: FieldSelector = FieldSelector()
|
||||
private var order: String? = null
|
||||
private var reverse: Boolean = false
|
||||
private var maxCount: Int? = null
|
||||
set(value) {
|
||||
when {
|
||||
value ?: 1 <= 0 -> return
|
||||
else -> field = value
|
||||
}
|
||||
}
|
||||
|
||||
fun fields(selector: FieldSelector.() -> Unit) {
|
||||
fields.selector()
|
||||
}
|
||||
|
||||
fun where(clause: ClauseMaker.() -> Clause) {
|
||||
where = ClauseMaker.clause().sqlStr
|
||||
}
|
||||
|
||||
fun select() =
|
||||
helper.select(adapter, fields.fieldName ?: "*", where, order, reverse, maxCount)
|
||||
|
||||
infix fun orderBy(field: String) {
|
||||
order = field.sqlStr
|
||||
}
|
||||
|
||||
infix fun orderBy(field: Field) {
|
||||
order = field.fieldName
|
||||
}
|
||||
|
||||
infix fun orderBy(field: KProperty<*>) {
|
||||
order = field.fieldName
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.reverse(reverse: Boolean) {
|
||||
order = fieldName
|
||||
this@SqlSelector.reverse = reverse
|
||||
}
|
||||
|
||||
infix fun Field.reverse(reverse: Boolean) {
|
||||
order = fieldName
|
||||
this@SqlSelector.reverse = reverse
|
||||
}
|
||||
|
||||
infix fun reverse(reverse: Boolean) {
|
||||
this.reverse = reverse
|
||||
}
|
||||
|
||||
infix fun limit(maxCount: Int?) {
|
||||
this.maxCount = maxCount
|
||||
}
|
||||
|
||||
infix fun Field.limit(maxCount: Int?) {
|
||||
order = fieldName
|
||||
this@SqlSelector.maxCount = maxCount
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.limit(maxCount: Int?) {
|
||||
order = fieldName
|
||||
this@SqlSelector.maxCount = maxCount
|
||||
}
|
||||
|
||||
infix fun Any.limit(maxCount: Int?) {
|
||||
this@SqlSelector.maxCount = maxCount
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import cn.tursom.database.SqlUtils.sqlStr
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.database.clauses.ClauseMaker
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class SqlUpdater(val helper: SqlHelper) {
|
||||
constructor(helper: SqlHelper, table: String) : this(helper) {
|
||||
this.table = table
|
||||
}
|
||||
|
||||
private lateinit var table: String
|
||||
private val set = HashMap<String, String>()
|
||||
private var where: String? = null
|
||||
|
||||
infix fun table(table: String) {
|
||||
this.table = table
|
||||
}
|
||||
|
||||
infix fun table(clazz: Class<*>) {
|
||||
table = clazz.tableName
|
||||
}
|
||||
|
||||
infix fun table(clazz: KClass<*>) {
|
||||
table = clazz.tableName
|
||||
}
|
||||
|
||||
infix fun Field.setTo(value: String) {
|
||||
set[fieldName] = value.sqlStr
|
||||
}
|
||||
|
||||
infix fun Field.setTo(value: Any) {
|
||||
set[fieldName] = value.toString()
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.setTo(value: String) {
|
||||
set[fieldName] = value.sqlStr
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.setTo(value: Any) {
|
||||
set[fieldName] = value.toString()
|
||||
}
|
||||
|
||||
infix operator fun Field.plus(value: Any) = +"$fieldName+$value"
|
||||
infix operator fun Field.minus(value: Any) = +"$fieldName-$value"
|
||||
infix operator fun Field.times(value: Any) = +"$fieldName*$value"
|
||||
infix operator fun Field.div(value: Any) = +"$fieldName/$value"
|
||||
infix operator fun Field.rem(value: Any) = +"$fieldName%$value"
|
||||
|
||||
infix operator fun KProperty<*>.plus(value: Any) = +"$fieldName+$value"
|
||||
infix operator fun KProperty<*>.minus(value: Any) = +"$fieldName-$value"
|
||||
infix operator fun KProperty<*>.times(value: Any) = +"$fieldName*$value"
|
||||
infix operator fun KProperty<*>.div(value: Any) = +"$fieldName/$value"
|
||||
infix operator fun KProperty<*>.rem(value: Any) = +"$fieldName%$value"
|
||||
|
||||
infix fun where(clause: ClauseMaker.() -> Clause) {
|
||||
where = ClauseMaker.clause().sqlStr
|
||||
}
|
||||
|
||||
operator fun String.unaryPlus() = StringUnit(this)
|
||||
|
||||
class StringUnit(private val str: String) {
|
||||
operator fun not() = str
|
||||
override fun toString() = str
|
||||
}
|
||||
|
||||
fun update(): Int {
|
||||
val set = StringBuilder()
|
||||
this.set.forEach { (field, value) ->
|
||||
set.append("$field=$value,")
|
||||
}
|
||||
if (set.isNotEmpty()) {
|
||||
set.delete(set.length - 1, set.length)
|
||||
}
|
||||
return helper.update(table, set.toString(), where ?: "")
|
||||
}
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.database.annotation.*
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.Iterable
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.contains
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.iterator
|
||||
import kotlin.collections.last
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
object SqlUtils {
|
||||
inline operator fun <T> invoke(action: SqlUtils.() -> T): T = this.action()
|
||||
|
||||
val Field.ignored get() = getAnnotation(Ignore::class.java) != null
|
||||
val Field.constructor get() = getAnnotation(Constructor::class.java)?.constructor
|
||||
|
||||
val Class<*>.dataField: List<Field>
|
||||
get() {
|
||||
val fields = declaredFields
|
||||
val fieldList = ArrayList<Field>()
|
||||
fields.forEach {
|
||||
if (!it.ignored) fieldList.add(it)
|
||||
}
|
||||
return fieldList
|
||||
}
|
||||
val Field.fieldName: String
|
||||
get() = getAnnotation(FieldName::class.java)?.name ?: name
|
||||
|
||||
val KProperty<*>.fieldName: String
|
||||
get() = javaField!!.fieldName
|
||||
|
||||
val Any.fieldValue: String
|
||||
get() = when (this) {
|
||||
is SqlField<*> -> javaClass.getAnnotation(StringField::class.java)?.let {
|
||||
sqlValue.sqlStr
|
||||
} ?: sqlValue
|
||||
is String -> sqlStr
|
||||
else -> toString()
|
||||
}
|
||||
|
||||
val Iterable<String>.fieldName: String?
|
||||
get() {
|
||||
val stringBuffer = StringBuffer()
|
||||
forEach {
|
||||
if (it.isNotEmpty())
|
||||
stringBuffer.append("`$it`,")
|
||||
}
|
||||
return if (stringBuffer.isNotEmpty()) {
|
||||
stringBuffer.delete(stringBuffer.length - 1, stringBuffer.length)
|
||||
stringBuffer.toString()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val Class<*>.isSqlField
|
||||
get() = interfaces.contains(SqlField::class.java)
|
||||
|
||||
val Any.tableName: String
|
||||
get() = javaClass.tableName
|
||||
|
||||
val Class<*>.tableName: String
|
||||
get() = (getAnnotation<TableName>()?.name ?: name.split('.').last()).toLowerCase()
|
||||
|
||||
|
||||
val KClass<*>.tableName: String
|
||||
get() = java.tableName
|
||||
|
||||
inline fun <reified T : Annotation> Field.getAnnotation(): T? = getAnnotation(T::class.java)
|
||||
|
||||
inline fun <reified T : Annotation> Class<*>.getAnnotation(): T? = getAnnotation(T::class.java)
|
||||
|
||||
fun Array<out Field>.fieldStr(): String {
|
||||
val fields = StringBuilder()
|
||||
forEach field@{ field ->
|
||||
if (field.ignored) return@field
|
||||
field.isAccessible = true
|
||||
fields.append("${field.fieldName},")
|
||||
}
|
||||
fields.deleteCharAt(fields.length - 1)
|
||||
return fields.toString()
|
||||
}
|
||||
|
||||
fun Iterable<String>.fieldStr(): String {
|
||||
val stringBuffer = StringBuffer()
|
||||
forEach {
|
||||
if (it.isNotEmpty())
|
||||
stringBuffer.append("$it,")
|
||||
}
|
||||
stringBuffer.delete(stringBuffer.length - 1, stringBuffer.length)
|
||||
return stringBuffer.toString()
|
||||
}
|
||||
|
||||
fun Class<*>.valueStr(value: Any): String? {
|
||||
val values = StringBuilder()
|
||||
declaredFields.forEach field@{ field ->
|
||||
field.isAccessible = true
|
||||
values.append(field.getAnnotation(Getter::class.java)?.let {
|
||||
getDeclaredMethod(field.name).invoke(null) as String
|
||||
} ?: field.get(value)?.fieldValue)
|
||||
values.append(',')
|
||||
}
|
||||
if (values.isNotEmpty()) {
|
||||
values.deleteCharAt(values.length - 1)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
return values.toString()
|
||||
}
|
||||
|
||||
fun Array<out Field>.valueStr(value: Any): String? {
|
||||
val clazz = value.javaClass
|
||||
val values = StringBuilder()
|
||||
forEach field@{ field ->
|
||||
if (field.ignored) return@field
|
||||
field.isAccessible = true
|
||||
val getter = field.getAnnotation(Getter::class.java)
|
||||
if (getter != null) {
|
||||
val method = clazz.getDeclaredMethod(getter.getter)
|
||||
method.isAccessible = true
|
||||
try {
|
||||
values.append(method.invoke(value))
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.targetException
|
||||
}
|
||||
values.append(',')
|
||||
} else {
|
||||
values.append(field.get(value)?.fieldValue)
|
||||
values.append(',')
|
||||
}
|
||||
}
|
||||
if (values.isNotEmpty()) {
|
||||
values.deleteCharAt(values.length - 1)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
return values.toString()
|
||||
}
|
||||
|
||||
fun Iterable<*>.valueStr(sqlFieldMap: Array<out Field>): String? {
|
||||
val values = StringBuilder()
|
||||
forEach { value ->
|
||||
value ?: return@forEach
|
||||
values.append("(${sqlFieldMap.valueStr(value) ?: return@forEach}),")
|
||||
}
|
||||
if (values.isNotEmpty()) {
|
||||
values.deleteCharAt(values.length - 1)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
return values.toString()
|
||||
}
|
||||
|
||||
fun Iterable<SqlFieldData>.valueStr(value: Iterable<*>): String? {
|
||||
val values = StringBuilder()
|
||||
forEach field@{ (field, _) ->
|
||||
field.isAccessible = true
|
||||
}
|
||||
value.forEach { obj ->
|
||||
values.append('(')
|
||||
val iterator = iterator()
|
||||
if (!iterator.hasNext()) return@forEach
|
||||
iterator.next().let { (field, getter) ->
|
||||
values.append(getter?.invoke(obj) ?: field.get(obj)?.fieldValue)
|
||||
}
|
||||
for ((field, getter) in iterator) {
|
||||
values.append(',')
|
||||
values.append(getter?.invoke(obj) ?: field.get(obj)?.fieldValue)
|
||||
}
|
||||
values.append("),")
|
||||
}
|
||||
if (values.isNotEmpty()) {
|
||||
values.deleteCharAt(values.length - 1)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
return values.toString()
|
||||
}
|
||||
|
||||
fun List<Pair<String, String>>.fieldStr(): Pair<String, String> {
|
||||
val first = StringBuilder()
|
||||
val second = StringBuilder()
|
||||
forEach { (f, s) ->
|
||||
first.append("$f,")
|
||||
second.append("$s,")
|
||||
}
|
||||
if (first.isNotEmpty()) first.deleteCharAt(first.length - 1)
|
||||
if (second.isNotEmpty()) second.deleteCharAt(second.length - 1)
|
||||
return first.toString() to second.toString()
|
||||
}
|
||||
|
||||
fun StringBuilder.appendField(
|
||||
field: Field,
|
||||
fieldType: Field.() -> String?,
|
||||
foreignKeyList: AbstractCollection<Pair<String, String>>,
|
||||
autoIncrement: String = "AUTO_INCREMENT",
|
||||
primaryKey: Field.() -> Unit
|
||||
) {
|
||||
if (field.ignored) return
|
||||
val fieldName = field.fieldName
|
||||
append("`$fieldName` ${field.fieldType() ?: return}")
|
||||
field.annotations.forEach annotations@{ annotation ->
|
||||
append(" ${when (annotation) {
|
||||
is NotNull -> "NOT NULL"
|
||||
is AutoIncrement -> autoIncrement
|
||||
is Unique -> "UNIQUE"
|
||||
is Default -> "DEFAULT ${annotation.default}"
|
||||
is Check -> "CHECK(${field.fieldName}${annotation.func})"
|
||||
is ExtraAttribute -> annotation.attributes
|
||||
is ForeignKey -> {
|
||||
foreignKeyList.add(fieldName to if (annotation.target.isNotEmpty()) annotation.target else fieldName)
|
||||
return@annotations
|
||||
}
|
||||
is PrimaryKey -> {
|
||||
field.primaryKey()
|
||||
return@annotations
|
||||
}
|
||||
else -> return@annotations
|
||||
}}")
|
||||
}
|
||||
append(',')
|
||||
}
|
||||
|
||||
val String.sqlStr
|
||||
get() = "'${replace("'", "''")}'"
|
||||
}
|
93
database/src/main/kotlin/cn/tursom/database/StringPool.kt
Normal file
93
database/src/main/kotlin/cn/tursom/database/StringPool.kt
Normal file
@ -0,0 +1,93 @@
|
||||
package cn.tursom.database
|
||||
|
||||
/**
|
||||
* Copy to jodd.util
|
||||
*
|
||||
*
|
||||
* Pool of `String` constants to prevent repeating of
|
||||
* hard-coded `String` literals in the code.
|
||||
* Due to fact that these are `public static final`
|
||||
* they will be inlined by java compiler and
|
||||
* reference to this class will be dropped.
|
||||
* There is **no** performance gain of using this pool.
|
||||
* Read: https://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.5
|
||||
*
|
||||
* * Literal strings within the same class in the same package represent references to the same `String` object.
|
||||
* * Literal strings within different classes in the same package represent references to the same `String` object.
|
||||
* * Literal strings within different classes in different packages likewise represent references to the same `String` object.
|
||||
* * Strings computed by constant expressions are computed at compile time and then treated as if they were literals.
|
||||
* * Strings computed by concatenation at run time are newly created and therefore distinct.
|
||||
*
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused", "SpellCheckingInspection")
|
||||
object StringPool {
|
||||
const val AMPERSAND = "&"
|
||||
const val AND = "and"
|
||||
const val AT = "@"
|
||||
const val ASTERISK = "*"
|
||||
const val STAR = ASTERISK
|
||||
const val BACK_SLASH = "\\"
|
||||
const val COLON = ":"
|
||||
const val COMMA = ","
|
||||
const val DASH = "-"
|
||||
const val DOLLAR = "$"
|
||||
const val DOT = "."
|
||||
const val DOTDOT = ".."
|
||||
const val DOT_CLASS = ".class"
|
||||
const val DOT_JAVA = ".java"
|
||||
const val DOT_XML = ".xml"
|
||||
const val EMPTY = ""
|
||||
const val EQUALS = "="
|
||||
const val FALSE = "false"
|
||||
const val SLASH = "/"
|
||||
const val HASH = "#"
|
||||
const val HAT = "^"
|
||||
const val LEFT_BRACE = "{"
|
||||
const val LEFT_BRACKET = "("
|
||||
const val LEFT_CHEV = "<"
|
||||
const val DOT_NEWLINE = ",\n"
|
||||
const val NEWLINE = "\n"
|
||||
const val N = "n"
|
||||
const val NO = "no"
|
||||
const val NULL = "null"
|
||||
const val OFF = "off"
|
||||
const val ON = "on"
|
||||
const val PERCENT = "%"
|
||||
const val PIPE = "|"
|
||||
const val PLUS = "+"
|
||||
const val QUESTION_MARK = "?"
|
||||
const val EXCLAMATION_MARK = "!"
|
||||
const val QUOTE = "\""
|
||||
const val RETURN = "\r"
|
||||
const val TAB = "\t"
|
||||
const val RIGHT_BRACE = "}"
|
||||
const val RIGHT_BRACKET = ")"
|
||||
const val RIGHT_CHEV = ">"
|
||||
const val SEMICOLON = ";"
|
||||
const val SINGLE_QUOTE = "'"
|
||||
const val BACKTICK = "`"
|
||||
const val SPACE = " "
|
||||
const val TILDA = "~"
|
||||
const val LEFT_SQ_BRACKET = "["
|
||||
const val RIGHT_SQ_BRACKET = "]"
|
||||
const val TRUE = "true"
|
||||
const val UNDERSCORE = "_"
|
||||
const val UTF_8 = "UTF-8"
|
||||
const val US_ASCII = "US-ASCII"
|
||||
const val ISO_8859_1 = "ISO-8859-1"
|
||||
const val Y = "y"
|
||||
const val YES = "yes"
|
||||
const val ONE = "1"
|
||||
const val ZERO = "0"
|
||||
const val DOLLAR_LEFT_BRACE = "\${"
|
||||
const val HASH_LEFT_BRACE = "#{"
|
||||
const val CRLF = "\r\n"
|
||||
const val HTML_NBSP = " "
|
||||
const val HTML_AMP = "&"
|
||||
const val HTML_QUOTE = """
|
||||
const val HTML_LT = "<"
|
||||
const val HTML_GT = ">"
|
||||
// ---------------------------------------------------------------- array
|
||||
val EMPTY_ARRAY = arrayOfNulls<String>(0)
|
||||
val BYTES_NEW_LINE = NEWLINE.toByteArray()
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class AutoIncrement
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class Check(val func: String)
|
@ -1,19 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
|
||||
/**
|
||||
* callback interface:
|
||||
*
|
||||
* constructor(value: String): FieldType
|
||||
*
|
||||
* or :
|
||||
*
|
||||
* for AsyncSqlAdapter:
|
||||
* |- constructor(value: JsonArray, index: Int): FieldValue?
|
||||
*
|
||||
* for SqlAdapter:
|
||||
* |- constructor(resultSet: ResultSet): FieldValue?
|
||||
*/
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class Constructor(val constructor: String)
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class Default(val default: String)
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class ExtraAttribute(val attributes: String)
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION)
|
||||
annotation class FieldName(val name: String)
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FIELD)
|
||||
annotation class FieldType(val name: String)
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD, AnnotationTarget.CLASS)
|
||||
annotation class ForeignKey(val target: String = "")
|
@ -1,9 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
/**
|
||||
* callback interface :
|
||||
* getter(): Any?
|
||||
*/
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class Getter(val getter: String)
|
@ -1,4 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class Ignore
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class NotNull
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class PrimaryKey
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class StringField
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class TableName(val name: String)
|
@ -1,8 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
/**
|
||||
* only for string
|
||||
*/
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class TextLength(val length: Int)
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.annotation
|
||||
|
||||
@MustBeDocumented
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class Unique
|
@ -0,0 +1,6 @@
|
||||
package cn.tursom.database.annotations
|
||||
|
||||
@MustBeDocumented
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
annotation class Param(val value: String)
|
@ -1,6 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
class AndClause(first: Clause, second: Clause) : Clause {
|
||||
override val sqlStr = "(${first.sqlStr} AND ${second.sqlStr})"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
interface Clause {
|
||||
val sqlStr: String
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.sqlStr
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.core.regex.RegexMaker
|
||||
import cn.tursom.core.regex.RegexUnit
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* 我想有点kotlin基础的人都能看懂怎么用的
|
||||
* 所有我就不写说明书了,只举个例子
|
||||
* clause { (!TestClass::text regexp { beg + +"还行" + end }) or (!TestClass::_id equal "10") }
|
||||
*/
|
||||
object ClauseMaker {
|
||||
// operator fun Field.unaryMinus() = fieldName
|
||||
// operator fun KProperty<*>.unaryMinus() = fieldName
|
||||
// operator fun Field.unaryPlus() = fieldName
|
||||
// operator fun KProperty<*>.unaryPlus() = fieldName
|
||||
operator fun Any.not() = this.toString()
|
||||
|
||||
operator fun String.not() = this.sqlStr
|
||||
operator fun Field.not() = fieldName
|
||||
operator fun KProperty<*>.not() = fieldName
|
||||
|
||||
infix fun Clause.and(clause: Clause) = AndClause(this, clause)
|
||||
infix operator fun Clause.plus(clause: Clause) = AndClause(this, clause)
|
||||
infix fun String.equal(value: String) = EqualClause(this, value)
|
||||
infix fun String.glob(value: String) = GlobClause(this, value)
|
||||
infix fun String.glob(maker: GlobClause.GlobValue.() -> String) = GlobClause(this, GlobClause.GlobValue.maker())
|
||||
infix fun String.greaterEqual(value: String) = GreaterEqualClause(this, value)
|
||||
infix fun String.greaterThan(value: String) = GreaterThanClause(this, value)
|
||||
infix fun String.lessEqual(value: String) = LessEqualClause(this, value)
|
||||
infix fun String.lessThan(value: String) = LessThanClause(this, value)
|
||||
infix fun String.like(value: String) = LikeClause(this, value)
|
||||
infix fun String.like(value: LikeClause.LikeWildcard.() -> String) = LikeClause(this, value)
|
||||
operator fun Clause.not() = NotClause(this)
|
||||
infix fun String.notEqual(value: String) = NotEqualClause(this, value)
|
||||
infix fun Clause.or(value: Clause) = OrClause(this, value)
|
||||
infix operator fun Clause.minus(value: Clause) = OrClause(this, value)
|
||||
// infix operator fun Clause.rangeTo(value: Clause) = OrClause(this, value)
|
||||
|
||||
infix fun String.regexp(value: String) = RegexpClause(this, value)
|
||||
infix fun String.regexp(value: Regex) = RegexpClause(this, value)
|
||||
infix fun String.regexp(value: RegexUnit) = RegexpClause(this, value)
|
||||
infix fun String.regexp(value: RegexMaker.() -> RegexUnit) = RegexpClause(this, value)
|
||||
|
||||
@Suppress("UNUSED_EXPRESSION")
|
||||
fun make(maker: ClauseMaker.() -> Clause) = maker()
|
||||
|
||||
inline operator fun invoke(maker: ClauseMaker.() -> Clause) = this.maker().sqlStr
|
||||
}
|
||||
|
||||
fun clause(maker: ClauseMaker.() -> Clause) = ClauseMaker.maker()
|
@ -1,14 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
class EqualClause(field: String, value: String) : Clause {
|
||||
constructor(field: Field, value: String) : this(field.fieldName, value)
|
||||
constructor(field: KProperty<*>, value: String) : this(field.javaField!!, value)
|
||||
|
||||
override val sqlStr = "$field=$value"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.sqlStr
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
class GlobClause(field: String, value: String) : Clause {
|
||||
constructor(field: Field, value: String) : this(field.fieldName, value)
|
||||
constructor(field: KProperty<*>, value: String) : this(field.javaField!!, value)
|
||||
|
||||
override val sqlStr = "$field GLOB ${value.sqlStr}"
|
||||
override fun toString() = sqlStr
|
||||
|
||||
object GlobValue {
|
||||
const val one = '*'
|
||||
const val any = '*'
|
||||
@Suppress("UNUSED_EXPRESSION")
|
||||
fun make(maker: GlobValue.() -> String) = maker()
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
class GreaterEqualClause(field: String, value: String) : Clause {
|
||||
constructor(field: Field, value: String) : this(field.fieldName, value)
|
||||
constructor(field: KProperty<*>, value: String) : this(field.javaField!!, value)
|
||||
|
||||
override val sqlStr = "$field>=$value"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
class GreaterThanClause(field: String, value: String) : Clause {
|
||||
constructor(field: Field, value: String) : this(field.fieldName, value)
|
||||
constructor(field: KProperty<*>, value: String) : this(field.javaField!!, value)
|
||||
|
||||
override val sqlStr = "$field>$value"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
class InfixExpressionClause(field: String, value: String, expression: String) : Clause {
|
||||
constructor(field: Field, value: String, expression: String) : this(field.fieldName, value, expression)
|
||||
constructor(field: KProperty<*>, value: String, expression: String) : this(field.javaField!!, value, expression)
|
||||
|
||||
override val sqlStr = "$field$expression$value"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
class LessEqualClause(field: String, value: String) : Clause {
|
||||
constructor(field: Field, value: String) : this(field.fieldName, value)
|
||||
constructor(field: KProperty<*>, value: String) : this(field.javaField!!, value)
|
||||
|
||||
override val sqlStr = "$field<=$value"
|
||||
override fun toString() = sqlStr
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
class LessThanClause(field: String, value: String) : Clause {
|
||||
constructor(field: Field, value: String) : this(field.fieldName, value)
|
||||
constructor(field: KProperty<*>, value: String) : this(field.javaField!!, value)
|
||||
|
||||
override val sqlStr = "$field<$value"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.sqlStr
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
class LikeClause(field: String, value: String) : Clause {
|
||||
constructor(field: Field, value: String) : this(field.fieldName, value)
|
||||
constructor(field: KProperty<*>, value: String) : this(field.javaField!!, value)
|
||||
|
||||
constructor(field: String, value: LikeWildcard.() -> String)
|
||||
: this(field, LikeWildcard.value())
|
||||
|
||||
constructor(field: Field, value: LikeWildcard.() -> String)
|
||||
: this(field, LikeWildcard.value())
|
||||
|
||||
constructor(field: KProperty<*>, value: LikeWildcard.() -> String)
|
||||
: this(field, LikeWildcard.value())
|
||||
|
||||
override val sqlStr = "$field LIKE '${value.sqlStr}'"
|
||||
override fun toString() = sqlStr
|
||||
|
||||
object LikeWildcard {
|
||||
val single: String = "_"
|
||||
val many: String = "%"
|
||||
fun charList(charList: String) = "[$charList]"
|
||||
fun unCharList(charList: String) = "[!$charList]"
|
||||
infix operator fun String.rangeTo(target: String) = "$this-$target"
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
class NotClause(clause: Clause) : Clause {
|
||||
override val sqlStr = "(NOT ${clause.sqlStr})"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
class NotEqualClause(field: String, value: String) : Clause {
|
||||
constructor(field: Field, value: String) : this(field.fieldName, value)
|
||||
constructor(field: KProperty<*>, value: String) : this(field.javaField!!, value)
|
||||
|
||||
override val sqlStr = "$field<>$value"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
class OrClause(first: Clause, second: Clause) : Clause {
|
||||
override val sqlStr = "(${first.sqlStr} OR ${second.sqlStr})"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package cn.tursom.database.clauses
|
||||
|
||||
import cn.tursom.database.SqlUtils.sqlStr
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.core.regex.RegexMaker
|
||||
import cn.tursom.core.regex.RegexUnit
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
|
||||
class RegexpClause(val field: String, val value: String) : Clause {
|
||||
constructor(field: Field, value: String) : this(field.fieldName, value)
|
||||
constructor(field: KProperty<*>, value: String) : this(field.javaField!!, value)
|
||||
|
||||
constructor(field: String, value: Regex) : this(field, value.toString())
|
||||
constructor(field: Field, value: Regex) : this(field, value.toString())
|
||||
constructor(field: KProperty<*>, value: Regex) : this(field, value.toString())
|
||||
|
||||
constructor(field: String, value: RegexUnit) : this(field, value.toString())
|
||||
constructor(field: Field, value: RegexUnit) : this(field, value.toString())
|
||||
constructor(field: KProperty<*>, value: RegexUnit) : this(field, value.toString())
|
||||
|
||||
constructor(field: String, value: RegexMaker.() -> RegexUnit) : this(field, RegexMaker.value())
|
||||
constructor(field: Field, value: RegexMaker.() -> RegexUnit) : this(field, RegexMaker.value())
|
||||
constructor(field: KProperty<*>, value: RegexMaker.() -> RegexUnit) : this(field, RegexMaker.value())
|
||||
|
||||
override val sqlStr = "$field REGEXP ${value.sqlStr}"
|
||||
override fun toString() = sqlStr
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
package cn.tursom.database.connpool
|
||||
|
||||
import cn.tursom.database.SqlAdapter
|
||||
import cn.tursom.database.SqlHelper
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import java.lang.reflect.Field
|
||||
|
||||
interface ConnectionPool<T : SqlHelper> : SqlHelper {
|
||||
fun getConnection(): T
|
||||
fun returnConnection(conn: T)
|
||||
|
||||
override val closed: Boolean get() = getConnection().closed
|
||||
|
||||
override fun createTable(table: String, keys: Iterable<String>) = use {
|
||||
it.createTable(table, keys)
|
||||
}
|
||||
|
||||
|
||||
override fun createTable(fields: Class<*>) = use {
|
||||
it.createTable(fields)
|
||||
}
|
||||
|
||||
override fun deleteTable(table: String) = use {
|
||||
it.deleteTable(table)
|
||||
}
|
||||
|
||||
override fun dropTable(table: String) = use {
|
||||
it.dropTable(table)
|
||||
}
|
||||
|
||||
override fun <T : Any> select(
|
||||
adapter: SqlAdapter<T>,
|
||||
fields: Iterable<String>?,
|
||||
where: Clause, order: Field?,
|
||||
reverse: Boolean,
|
||||
maxCount: Int?
|
||||
): SqlAdapter<T> = use {
|
||||
it.select(adapter, fields, where, order, reverse, maxCount)
|
||||
}
|
||||
|
||||
override fun <T : Any> select(
|
||||
adapter: SqlAdapter<T>,
|
||||
fields: String,
|
||||
where: String?,
|
||||
order: String?,
|
||||
reverse: Boolean,
|
||||
maxCount: Int?
|
||||
): SqlAdapter<T> = use {
|
||||
it.select(adapter, fields, where, order, reverse, maxCount)
|
||||
}
|
||||
|
||||
override fun insert(value: Any): Int = use {
|
||||
it.insert(value)
|
||||
}
|
||||
|
||||
override fun insert(valueList: Iterable<*>): Int = use {
|
||||
it.insert(valueList)
|
||||
}
|
||||
|
||||
override fun insert(table: String, fields: String, values: String): Int = use {
|
||||
it.insert(table, fields, values)
|
||||
}
|
||||
|
||||
override fun update(table: String, set: String, where: String): Int = use {
|
||||
it.update(table, set, where)
|
||||
}
|
||||
|
||||
override fun update(value: Any, where: Clause): Int = use {
|
||||
it.update(value, where)
|
||||
}
|
||||
|
||||
override fun delete(table: String, where: String?): Int = use {
|
||||
it.delete(table, where)
|
||||
}
|
||||
|
||||
override fun delete(table: String, where: Clause?): Int = use {
|
||||
it.delete(table, where)
|
||||
}
|
||||
|
||||
override fun commit() = use {
|
||||
it.commit()
|
||||
}
|
||||
|
||||
override fun close() = use {
|
||||
it.close()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T : SqlHelper, T1> ConnectionPool<T>.use(action: (helper: T) -> T1): T1 {
|
||||
val helper = getConnection()
|
||||
val ret = action(helper)
|
||||
returnConnection(helper)
|
||||
return ret
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package cn.tursom.database.connpool
|
||||
|
||||
import cn.tursom.database.SqlHelper
|
||||
import cn.tursom.core.pool.LinkedPool
|
||||
|
||||
class SqlHelperPool<T : SqlHelper>(
|
||||
private val newConnection: () -> T
|
||||
) : ConnectionPool<T> {
|
||||
private val pool = LinkedPool<T>()
|
||||
|
||||
override fun getConnection(): T {
|
||||
var conn = pool.get()
|
||||
while (conn != null && conn.closed) conn = pool.get()
|
||||
return conn ?: newConnection()
|
||||
}
|
||||
|
||||
override fun returnConnection(conn: T) {
|
||||
if (!conn.closed) {
|
||||
pool.put(conn)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import com.mongodb.MongoClient
|
||||
|
||||
//TODO
|
||||
class MongoHelper(private val mongoClient: MongoClient) {
|
||||
constructor(host: String = "localhost", port: Int = 27017) : this(MongoClient(host, port))
|
||||
}
|
@ -1,322 +0,0 @@
|
||||
package cn.tursom.database.mysql
|
||||
|
||||
import cn.tursom.database.*
|
||||
import cn.tursom.database.SqlUtils.fieldStr
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import cn.tursom.database.SqlUtils.appendField
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.SqlUtils.valueStr
|
||||
import cn.tursom.database.SqlUtils.isSqlField
|
||||
import cn.tursom.database.SqlUtils.fieldValue
|
||||
import cn.tursom.database.SqlUtils.ignored
|
||||
import cn.tursom.database.annotation.FieldType
|
||||
import cn.tursom.database.annotation.ForeignKey
|
||||
import cn.tursom.database.annotation.Getter
|
||||
import cn.tursom.database.annotation.TextLength
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import java.lang.reflect.Field
|
||||
import java.sql.Connection
|
||||
import java.sql.DriverManager
|
||||
import java.sql.SQLSyntaxErrorException
|
||||
|
||||
/**
|
||||
* MySQLHelper,SQLite辅助使用类
|
||||
* 实现创建表格、查询、插入和更新功能
|
||||
*/
|
||||
@Suppress("SqlNoDataSourceInspection", "SqlDialectInspection")
|
||||
class MySqlHelper(
|
||||
@Suppress("MemberVisibilityCanBePrivate") val connection: Connection,
|
||||
base: String? = null
|
||||
) : SqlHelper {
|
||||
override val closed: Boolean get() = connection.isClosed
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
var basename: String? = null
|
||||
get() = synchronized(this) {
|
||||
return field
|
||||
}
|
||||
set(value) {
|
||||
value?.let { base ->
|
||||
synchronized(this) {
|
||||
val statement = connection.createStatement()
|
||||
statement.executeQuery("USE $base")
|
||||
field = base
|
||||
statement.closeOnCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
connection.autoCommit = false
|
||||
basename = base
|
||||
}
|
||||
|
||||
constructor(url: String, user: String, password: String, base: String? = null)
|
||||
: this(
|
||||
DriverManager.getConnection(
|
||||
"jdbc:mysql://$url?characterEncoding=utf-8&serverTimezone=UTC",
|
||||
user,
|
||||
password
|
||||
),
|
||||
base
|
||||
)
|
||||
|
||||
/*
|
||||
* 创建表格
|
||||
* table: 表格名
|
||||
* keys: 属性列表
|
||||
*/
|
||||
override fun createTable(table: String, keys: Iterable<String>) {
|
||||
val statement = connection.createStatement()
|
||||
statement.executeUpdate("CREATE TABLE if not exists `$table` ( ${keys.fieldStr()} ) ENGINE = InnoDB DEFAULT CHARSET=utf8;")
|
||||
connection.commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据提供的class对象自动化创建表格
|
||||
*/
|
||||
override fun createTable(fields: Class<*>) {
|
||||
createTable(fields.tableName, fields, "InnoDB", "utf8")
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据提供的class对象自动化创建表格
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
fun createTable(table: String, keys: Class<*>, engine: String = "InnoDB", charset: String = "utf8") {
|
||||
val statement = connection.createStatement()
|
||||
statement.executeUpdate(createTableStr(table, keys, engine, charset))
|
||||
connection.commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除表格
|
||||
*/
|
||||
override fun deleteTable(table: String) {
|
||||
val statement = connection.createStatement()
|
||||
statement.executeUpdate("DROP TABLE if exists $table ENGINE = InnoDB DEFAULT CHARSET=utf8;")
|
||||
connection.commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除表格
|
||||
*/
|
||||
override fun dropTable(table: String) {
|
||||
deleteTable(table)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param adapter 用于保存查询结果的数据类,由SQLAdapter继承而来
|
||||
* @param fields 查询字段
|
||||
* @param where 指定从一个表或多个表中获取数据的条件,Pair左边为字段名,右边为限定的值
|
||||
* @param maxCount 最大查询数量
|
||||
*/
|
||||
override fun <T : Any> select(
|
||||
adapter: SqlAdapter<T>,
|
||||
fields: Iterable<String>?,
|
||||
where: Clause,
|
||||
order: Field?,
|
||||
reverse: Boolean,
|
||||
maxCount: Int?
|
||||
): SqlAdapter<T> = select(
|
||||
adapter = adapter,
|
||||
fields = fields?.fieldStr() ?: "*",
|
||||
where = where.sqlStr,
|
||||
order = order?.fieldName,
|
||||
reverse = reverse,
|
||||
maxCount = maxCount
|
||||
)
|
||||
|
||||
|
||||
override fun <T : Any> select(
|
||||
adapter: SqlAdapter<T>,
|
||||
fields: String,
|
||||
where: String?,
|
||||
order: String?,
|
||||
reverse: Boolean,
|
||||
maxCount: Int?
|
||||
): SqlAdapter<T> {
|
||||
val sql = "SELECT $fields FROM ${adapter.clazz.tableName
|
||||
}${if (where != null) " WHERE $where" else ""
|
||||
}${if (order != null) " ORDER BY $order ${if (reverse) "DESC" else "ASC"}" else ""
|
||||
}${if (maxCount != null) " limit $maxCount" else ""
|
||||
};"
|
||||
val statement = connection.createStatement()
|
||||
adapter.adapt(statement.executeQuery(sql))
|
||||
statement.closeOnCompletion()
|
||||
return adapter
|
||||
}
|
||||
|
||||
override fun update(
|
||||
table: String,
|
||||
set: String,
|
||||
where: String
|
||||
): Int {
|
||||
val sql = "UPDATE $table SET $set${if (where.isNotEmpty()) " WHERE $where" else ""};"
|
||||
val statement = connection.createStatement()
|
||||
return try {
|
||||
statement.executeUpdate(sql)
|
||||
} finally {
|
||||
commit()
|
||||
statement.closeOnCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据库数据
|
||||
* @param value 用来存储数据的bean对象
|
||||
* @param where SQL语句的一部分,用来限定查找的条件。每一条String储存一个条件
|
||||
*/
|
||||
override fun update(value: Any, where: Clause): Int {
|
||||
val sb = StringBuilder()
|
||||
value.javaClass.declaredFields.forEach {
|
||||
it.isAccessible = true
|
||||
sb.append("${it.fieldName}=${it.get(value)?.fieldValue ?: return@forEach},")
|
||||
}
|
||||
if (sb.isNotEmpty())
|
||||
sb.delete(sb.length - 1, sb.length)
|
||||
return update(value.tableName, sb.toString(), where.sqlStr)
|
||||
}
|
||||
|
||||
private fun insert(connection: Connection, sql: String, table: Class<*>): Int {
|
||||
val statement = connection.createStatement()
|
||||
return try {
|
||||
statement.executeUpdate(sql)
|
||||
} catch (e: SQLSyntaxErrorException) {
|
||||
if (e.message == "Table '$basename.${table.tableName}' doesn't exist") {
|
||||
createTable(table)
|
||||
statement.executeUpdate(sql)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
} finally {
|
||||
connection.commit()
|
||||
statement.closeOnCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
override fun insert(table: String, fields: String, values: String): Int {
|
||||
val sql = "INSERT INTO $table ($fields) VALUES $values;"
|
||||
val statement = connection.createStatement()
|
||||
return try {
|
||||
statement.executeUpdate(sql)
|
||||
} finally {
|
||||
connection.commit()
|
||||
statement.closeOnCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
override fun insert(value: Any): Int {
|
||||
val clazz = value.javaClass
|
||||
val fields = clazz.declaredFields
|
||||
val sql = "INSERT INTO ${value.tableName} (${fields.fieldStr()}) VALUES (${clazz.valueStr(value) ?: return 0});"
|
||||
return insert(connection, sql, clazz)
|
||||
}
|
||||
|
||||
override fun insert(valueList: Iterable<*>): Int {
|
||||
val first = valueList.firstOrNull() ?: return 0
|
||||
val clazz = first.javaClass
|
||||
val fields = ArrayList<SqlFieldData>()
|
||||
clazz.declaredFields.forEach { field ->
|
||||
val getter = field.getAnnotation(Getter::class.java)?.let { clazz.getDeclaredMethod(field.name) }
|
||||
fields.add(SqlFieldData(field, getter))
|
||||
}
|
||||
val values = fields.valueStr(valueList) ?: return 0
|
||||
if (values.isEmpty()) return 0
|
||||
val sql = "INSERT INTO ${first.tableName} (${first.javaClass.declaredFields.fieldStr()}) VALUES $values;"
|
||||
return insert(connection, sql, clazz)
|
||||
}
|
||||
|
||||
override fun delete(table: String, where: String?): Int {
|
||||
val sql = "DELETE FROM `$table`${if (where != null) " WHERE $where" else ""};"
|
||||
val statement = connection.createStatement()
|
||||
return try {
|
||||
statement.executeUpdate(sql)
|
||||
} finally {
|
||||
connection.commit()
|
||||
statement.closeOnCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
override fun delete(table: String, where: Clause?) =
|
||||
delete(table, where?.sqlStr)
|
||||
|
||||
|
||||
override fun close() {
|
||||
connection.close()
|
||||
}
|
||||
|
||||
override fun commit() {
|
||||
connection.commit()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
init {
|
||||
Class.forName("com.mysql.cj.jdbc.Driver")
|
||||
}
|
||||
|
||||
fun createTableStr(keys: Class<*>, engine: String = "InnoDB", charset: String = "utf8"): String =
|
||||
createTableStr(keys.tableName, keys, engine, charset)
|
||||
|
||||
fun createTableStr(table: String, keys: Class<*>, engine: String = "InnoDB", charset: String = "utf8"): String {
|
||||
val fieldSet = keys.declaredFields
|
||||
val valueStrBuilder = StringBuilder()
|
||||
valueStrBuilder.append("CREATE TABLE IF NOT EXISTS `$table`(")
|
||||
val primaryKeySet = ArrayList<String>()
|
||||
|
||||
val foreignKey = keys.getAnnotation(ForeignKey::class.java)?.let {
|
||||
if (it.target.isNotEmpty()) it.target else null
|
||||
}
|
||||
val foreignKeyList = ArrayList<Pair<String, String>>()
|
||||
|
||||
fieldSet.forEach {
|
||||
if (it.ignored) return@forEach
|
||||
valueStrBuilder.appendField(it, { it.fieldType }, foreignKeyList) {
|
||||
primaryKeySet.add(fieldName)
|
||||
}
|
||||
}
|
||||
|
||||
if (primaryKeySet.isNotEmpty()) {
|
||||
valueStrBuilder.append("PRIMARY KEY(${primaryKeySet.fieldName}),")
|
||||
}
|
||||
|
||||
if (foreignKey != null && foreignKeyList.isEmpty()) {
|
||||
val (source, target) = foreignKeyList.fieldStr()
|
||||
valueStrBuilder.append("FOREIGN KEY ($source) REFERENCES $foreignKey ($target),")
|
||||
}
|
||||
valueStrBuilder.deleteCharAt(valueStrBuilder.length - 1)
|
||||
|
||||
valueStrBuilder.append(")ENGINE=$engine DEFAULT CHARSET=$charset;")
|
||||
return valueStrBuilder.toString()
|
||||
}
|
||||
|
||||
private val Field.fieldType: String?
|
||||
get() = getAnnotation(FieldType::class.java)?.name ?: when (type) {
|
||||
java.lang.Byte::class.java -> "TINYINT"
|
||||
java.lang.Character::class.java -> "TINYINT"
|
||||
java.lang.Short::class.java -> "SMALLINT"
|
||||
java.lang.Integer::class.java -> "INT"
|
||||
java.lang.Long::class.java -> "BIGINT"
|
||||
java.lang.Float::class.java -> "FLOAT"
|
||||
java.lang.Double::class.java -> "DOUBLE"
|
||||
|
||||
Byte::class.java -> "TINYINT"
|
||||
Char::class.java -> "TINYINT"
|
||||
Short::class.java -> "SMALLINT"
|
||||
Int::class.java -> "INT"
|
||||
Long::class.java -> "BIGINT"
|
||||
Float::class.java -> "FLOAT"
|
||||
Double::class.java -> "Double"
|
||||
|
||||
java.lang.String::class.java -> getAnnotation(TextLength::class.java)?.let { "CHAR(${it.length})" }
|
||||
?: "TEXT"
|
||||
else -> if (type.isSqlField) {
|
||||
type.getAnnotation(FieldType::class.java)?.name ?: type.name.split('.').last()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,333 +0,0 @@
|
||||
package cn.tursom.database.sqlite
|
||||
|
||||
import cn.tursom.database.SqlUtils.fieldName
|
||||
import cn.tursom.database.SqlUtils.fieldValue
|
||||
import cn.tursom.database.SqlUtils.isSqlField
|
||||
import cn.tursom.database.SqlUtils.ignored
|
||||
import cn.tursom.database.SqlUtils.valueStr
|
||||
import cn.tursom.database.SqlUtils.fieldStr
|
||||
import cn.tursom.database.SqlUtils.tableName
|
||||
import cn.tursom.database.SqlUtils.getAnnotation
|
||||
import cn.tursom.database.*
|
||||
import cn.tursom.database.annotation.*
|
||||
import cn.tursom.database.clauses.Clause
|
||||
import cn.tursom.core.simplifyPath
|
||||
import org.sqlite.SQLiteException
|
||||
import java.io.File
|
||||
import java.lang.reflect.Field
|
||||
import java.sql.Connection
|
||||
import java.sql.DriverManager
|
||||
import java.sql.SQLException
|
||||
|
||||
|
||||
/**
|
||||
* MySQLHelper,SQLite辅助使用类
|
||||
* 实现创建表格、查询、插入和更新功能
|
||||
*/
|
||||
|
||||
@Suppress("SqlDialectInspection", "SqlNoDataSourceInspection")
|
||||
open class SQLiteHelper
|
||||
/**
|
||||
* 创建名为 base.db 的数据库连接
|
||||
*/
|
||||
(base: String) : SqlHelper {
|
||||
private val connection: Connection
|
||||
private val path = File(base).absolutePath.simplifyPath()
|
||||
|
||||
init {
|
||||
synchronized(connectionMap) {
|
||||
connection = connectionMap[path] ?: {
|
||||
val connection = DriverManager.getConnection("jdbc:sqlite:$base") ?: throw CantConnectDataBase()
|
||||
connectionMap[path] = connection
|
||||
connection.autoCommit = false
|
||||
// 实现 REGEXP 函数
|
||||
org.sqlite.Function.create(connection, "REGEXP", regexp)
|
||||
connection
|
||||
}()
|
||||
connectionCount[path] = connectionCount[path] ?: 0 + 1
|
||||
}
|
||||
}
|
||||
|
||||
override val closed: Boolean get() = connection.isClosed
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
if (other is SQLiteHelper) {
|
||||
connection == other.connection
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
private fun doSql(sql: String): Int {
|
||||
val statement = connection.createStatement()
|
||||
return try {
|
||||
statement.executeUpdate(sql)
|
||||
} finally {
|
||||
commit()
|
||||
statement.closeOnCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表格
|
||||
* @param table: 表格名
|
||||
* @param keys: 属性列表
|
||||
*/
|
||||
override fun createTable(table: String, keys: Iterable<String>) {
|
||||
val sql = "CREATE TABLE if not exists $table (${keys.fieldStr()})"
|
||||
doSql(sql)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据提供的class对象自动化创建表格
|
||||
* 但是有诸多缺陷,所以不是很建议使用
|
||||
*/
|
||||
override fun createTable(fields: Class<*>) {
|
||||
val sql = createTableStr(fields)
|
||||
doSql(sql)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除表格
|
||||
*/
|
||||
override fun deleteTable(table: String) {
|
||||
val sql = "DROP TABLE if exists $table"
|
||||
doSql(sql)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除表格
|
||||
*/
|
||||
override fun dropTable(table: String) {
|
||||
deleteTable(table)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param adapter 用于保存查询结果的数据类,由SQLAdapter继承而来
|
||||
* @param fields 查询字段
|
||||
* @param where 指定从一个表或多个表中获取数据的条件,Pair左边为字段名,右边为限定的值
|
||||
* @param maxCount 最大查询数量
|
||||
*/
|
||||
override fun <T : Any> select(
|
||||
adapter: SqlAdapter<T>,
|
||||
fields: Iterable<String>?,
|
||||
where: Clause,
|
||||
order: Field?,
|
||||
reverse: Boolean,
|
||||
maxCount: Int?
|
||||
): SqlAdapter<T> =
|
||||
select(adapter, fields?.fieldStr() ?: "*", where.sqlStr, order?.fieldName, reverse, maxCount)
|
||||
|
||||
override fun <T : Any> select(
|
||||
adapter: SqlAdapter<T>, fields: String, where: String?, order: String?, reverse: Boolean, maxCount: Int?
|
||||
): SqlAdapter<T> {
|
||||
val sql = "SELECT $fields FROM ${adapter.clazz.tableName
|
||||
}${if (where != null) " WHERE $where" else ""
|
||||
}${if (order != null) " ORDER BY $order ${if (reverse) "DESC" else "ASC"}" else ""
|
||||
}${if (maxCount != null) " limit 0,$maxCount" else ""
|
||||
};"
|
||||
val statement = connection.createStatement()
|
||||
try {
|
||||
adapter.adapt(
|
||||
statement.executeQuery(sql)
|
||||
)
|
||||
} catch (e: SQLiteException) {
|
||||
if (e.message != "[SQLITE_ERROR] SQL error or missing cn.tusom.database (no such table: ${adapter.clazz.tableName})") throw e
|
||||
}
|
||||
statement.closeOnCompletion()
|
||||
return adapter
|
||||
}
|
||||
|
||||
private fun insert(connection: Connection, sql: String, table: Class<*>): Int {
|
||||
val statement = connection.createStatement()
|
||||
return try {
|
||||
statement.executeUpdate(sql)
|
||||
} catch (e: SQLiteException) {
|
||||
if (e.message == "[SQLITE_ERROR] SQL error or missing cn.tusom.database (no such table: ${table.tableName})") {
|
||||
createTable(table)
|
||||
statement.executeUpdate(sql)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
} finally {
|
||||
connection.commit()
|
||||
statement.closeOnCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
override fun insert(value: Any): Int {
|
||||
val clazz = value.javaClass
|
||||
val fields = clazz.declaredFields
|
||||
val column = fields.fieldStr()
|
||||
val valueStr = fields.valueStr(value) ?: return 0
|
||||
val sql = "INSERT INTO ${value.tableName} ($column) VALUES ($valueStr);"
|
||||
return insert(connection, sql, clazz)
|
||||
}
|
||||
|
||||
override fun insert(valueList: Iterable<*>): Int {
|
||||
val first = valueList.firstOrNull() ?: return 0
|
||||
val clazz = first.javaClass
|
||||
val fields = ArrayList<SqlFieldData>()
|
||||
clazz.declaredFields.forEach { field ->
|
||||
if (field.ignored) return@forEach
|
||||
val getter = field.getAnnotation(Getter::class.java)?.let { clazz.getDeclaredMethod(field.name) }
|
||||
fields.add(SqlFieldData(field, getter))
|
||||
}
|
||||
val values = fields.valueStr(valueList) ?: return 0
|
||||
if (values.isEmpty()) return 0
|
||||
val sql = "INSERT INTO ${first.tableName} (${clazz.declaredFields.fieldStr()}) VALUES $values;"
|
||||
return insert(connection, sql, clazz)
|
||||
}
|
||||
|
||||
override fun insert(table: String, fields: String, values: String): Int {
|
||||
val sql = "INSERT INTO $table ($fields) VALUES $values;"
|
||||
return doSql(sql)
|
||||
}
|
||||
|
||||
override fun update(table: String, set: String, where: String): Int {
|
||||
val sql = "UPDATE $table SET $set WHERE $where;"
|
||||
return doSql(sql)
|
||||
}
|
||||
|
||||
override fun update(
|
||||
value: Any, where: Clause
|
||||
): Int {
|
||||
val set = StringBuilder()
|
||||
value.javaClass.declaredFields.forEach {
|
||||
it.isAccessible = true
|
||||
it.get(value)?.let { value ->
|
||||
set.append("${it.fieldName}=${value.fieldValue},")
|
||||
}
|
||||
}
|
||||
if (set.isNotEmpty()) {
|
||||
set.delete(set.length - 1, set.length)
|
||||
}
|
||||
return update(value.tableName, set.toString(), where.sqlStr)
|
||||
}
|
||||
|
||||
override fun delete(table: String, where: String?): Int {
|
||||
val sql = "DELETE FROM $table${if (where?.isNotEmpty() == true) " WHERE $where" else ""};"
|
||||
return doSql(sql)
|
||||
}
|
||||
|
||||
override fun delete(table: String, where: Clause?): Int {
|
||||
return delete(table, where?.sqlStr)
|
||||
}
|
||||
|
||||
override fun commit() {
|
||||
synchronized(connection) {
|
||||
connection.commit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
synchronized(connectionMap) {
|
||||
connectionCount[path] = connectionCount[path] ?: 1 - 1
|
||||
if (connectionCount[path] == 0) {
|
||||
connectionCount.remove(path)
|
||||
connectionMap.remove(path)
|
||||
connection.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = connection.hashCode()
|
||||
result = 31 * result + path.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
class CantConnectDataBase(s: String? = null) : SQLException(s)
|
||||
|
||||
companion object {
|
||||
private val connectionMap by lazy {
|
||||
Class.forName("org.sqlite.JDBC")
|
||||
HashMap<String, Connection>()
|
||||
}
|
||||
private var connectionCount = HashMap<String, Int>()
|
||||
|
||||
private val Field.fieldType: String?
|
||||
get() = when (type) {
|
||||
java.lang.Byte::class.java -> "INTEGER"
|
||||
java.lang.Short::class.java -> "INTEGER"
|
||||
java.lang.Integer::class.java -> "INTEGER"
|
||||
java.lang.Long::class.java -> "INTEGER"
|
||||
java.lang.Float::class.java -> "REAL"
|
||||
java.lang.Double::class.java -> "REAL"
|
||||
|
||||
Byte::class.java -> "INTEGER"
|
||||
Char::class.java -> "INTEGER"
|
||||
Short::class.java -> "INTEGER"
|
||||
Int::class.java -> "INTEGER"
|
||||
Long::class.java -> "INTEGER"
|
||||
Float::class.java -> "REAL"
|
||||
Double::class.java -> "REAL"
|
||||
|
||||
java.lang.String::class.java -> getAnnotation<TextLength>()?.let { "CHAR(${it.length})" } ?: "TEXT"
|
||||
|
||||
else -> {
|
||||
if (type.isSqlField) {
|
||||
type.getAnnotation<FieldType>()?.name ?: type.name.split('.').last()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val regexp = object : org.sqlite.Function() {
|
||||
override fun xFunc() {
|
||||
val regex = Regex(value_text(0) ?: "")
|
||||
val value = value_text(1) ?: ""
|
||||
result(if (regex.containsMatchIn(value)) 1 else 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendField(
|
||||
field: Field,
|
||||
foreignKeyList: java.util.AbstractCollection<in Pair<String, String>>
|
||||
) {
|
||||
val fieldName = field.fieldName
|
||||
append("`$fieldName` ${field.fieldType ?: return}")
|
||||
field.annotations.forEach annotations@{ annotation ->
|
||||
append(" ${when (annotation) {
|
||||
is NotNull -> "NOT NULL"
|
||||
is Unique -> "UNIQUE"
|
||||
is Default -> "DEFAULT ${annotation.default}"
|
||||
is Check -> "CHECK(${field.fieldName}${annotation.func})"
|
||||
is ExtraAttribute -> annotation.attributes
|
||||
is PrimaryKey -> " PRIMARY KEY${field.getAnnotation(AutoIncrement::class.java)?.let { " AUTOINCREMENT" }
|
||||
?: ""}"
|
||||
is ForeignKey -> {
|
||||
foreignKeyList.add(fieldName to if (annotation.target.isNotEmpty()) annotation.target else fieldName)
|
||||
return@annotations
|
||||
}
|
||||
else -> return@annotations
|
||||
}}")
|
||||
}
|
||||
append(',')
|
||||
}
|
||||
|
||||
fun createTableStr(keys: Class<*>): String {
|
||||
val foreignKey = keys.getAnnotation(ForeignKey::class.java)?.let {
|
||||
if (it.target.isNotEmpty()) it.target else null
|
||||
}
|
||||
val foreignKeyList = ArrayList<Pair<String, String>>()
|
||||
|
||||
val valueStrBuilder = StringBuilder()
|
||||
valueStrBuilder.append("CREATE TABLE IF NOT EXISTS ${keys.tableName}(")
|
||||
keys.declaredFields.forEach {
|
||||
valueStrBuilder.appendField(it, foreignKeyList)
|
||||
}
|
||||
|
||||
foreignKey?.let {
|
||||
if (foreignKeyList.isEmpty()) return@let
|
||||
val (source, target) = foreignKeyList.fieldStr()
|
||||
valueStrBuilder.append("FOREIGN KEY ($source) REFERENCES $it ($target),")
|
||||
}
|
||||
|
||||
valueStrBuilder.deleteCharAt(valueStrBuilder.length - 1)
|
||||
valueStrBuilder.append(");")
|
||||
return valueStrBuilder.toString()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package cn.tursom.database.wrapper
|
||||
|
||||
interface AbstractWrapper<T, Children : AbstractWrapper<T, Children>> :
|
||||
Wrapper<T>,
|
||||
Compare<Children, T>,
|
||||
Nested<Children, Children>,
|
||||
Join<Children>,
|
||||
Func<Children, T>
|
@ -0,0 +1,86 @@
|
||||
package cn.tursom.database.wrapper
|
||||
|
||||
import java.io.Serializable
|
||||
import java.util.function.BiPredicate
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
interface Compare<Children, T> : Serializable {
|
||||
fun <V> allEq(params: Map<KProperty1<T,*>, V>?): Children {
|
||||
return this.allEq(params, true)
|
||||
}
|
||||
|
||||
fun <V> allEq(params: Map<KProperty1<T,*>, V>?, null2IsNull: Boolean): Children {
|
||||
return this.allEq(true, params, null2IsNull)
|
||||
}
|
||||
|
||||
fun <V> allEq(condition: Boolean, params: Map<KProperty1<T,*>, V>?, null2IsNull: Boolean): Children
|
||||
fun <V> allEq(filter: BiPredicate<KProperty1<T,*>, V>?, params: Map<KProperty1<T,*>, V>?): Children {
|
||||
return this.allEq(filter, params, true)
|
||||
}
|
||||
|
||||
fun <V> allEq(filter: BiPredicate<KProperty1<T,*>, V>?, params: Map<KProperty1<T,*>, V>?, null2IsNull: Boolean): Children {
|
||||
return this.allEq(true, filter, params, null2IsNull)
|
||||
}
|
||||
|
||||
fun <V> allEq(condition: Boolean, filter: BiPredicate<KProperty1<T,*>, V>?, params: Map<KProperty1<T,*>, V>?, null2IsNull: Boolean): Children
|
||||
fun eq(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.eq(true, column, `val`)
|
||||
}
|
||||
|
||||
fun eq(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
fun ne(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.ne(true, column, `val`)
|
||||
}
|
||||
|
||||
fun ne(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
fun gt(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.gt(true, column, `val`)
|
||||
}
|
||||
|
||||
fun gt(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
fun ge(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.ge(true, column, `val`)
|
||||
}
|
||||
|
||||
fun ge(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
fun lt(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.lt(true, column, `val`)
|
||||
}
|
||||
|
||||
fun lt(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
fun le(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.le(true, column, `val`)
|
||||
}
|
||||
|
||||
fun le(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
fun between(column: KProperty1<T,*>, val1: Any?, val2: Any?): Children {
|
||||
return this.between(true, column, val1, val2)
|
||||
}
|
||||
|
||||
fun between(condition: Boolean, column: KProperty1<T,*>, val1: Any?, val2: Any?): Children
|
||||
fun notBetween(column: KProperty1<T,*>, val1: Any?, val2: Any?): Children {
|
||||
return this.notBetween(true, column, val1, val2)
|
||||
}
|
||||
|
||||
fun notBetween(condition: Boolean, column: KProperty1<T,*>, val1: Any?, val2: Any?): Children
|
||||
fun like(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.like(true, column, `val`)
|
||||
}
|
||||
|
||||
fun like(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
fun notLike(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.notLike(true, column, `val`)
|
||||
}
|
||||
|
||||
fun notLike(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
fun likeLeft(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.likeLeft(true, column, `val`)
|
||||
}
|
||||
|
||||
fun likeLeft(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
fun likeRight(column: KProperty1<T,*>, `val`: Any?): Children {
|
||||
return this.likeRight(true, column, `val`)
|
||||
}
|
||||
|
||||
fun likeRight(condition: Boolean, column: KProperty1<T,*>, `val`: Any?): Children
|
||||
}
|
92
database/src/main/kotlin/cn/tursom/database/wrapper/Func.kt
Normal file
92
database/src/main/kotlin/cn/tursom/database/wrapper/Func.kt
Normal file
@ -0,0 +1,92 @@
|
||||
package cn.tursom.database.wrapper
|
||||
|
||||
import java.io.Serializable
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
interface Func<Children, T> : Serializable {
|
||||
fun isNull(column: KProperty1<T, *>): Children {
|
||||
return this.isNull(true, column)
|
||||
}
|
||||
|
||||
fun isNull(condition: Boolean, column: KProperty1<T,*>): Children
|
||||
fun isNotNull(column: KProperty1<T,*>): Children {
|
||||
return this.isNotNull(true, column)
|
||||
}
|
||||
|
||||
fun isNotNull(condition: Boolean, column: KProperty1<T,*>): Children
|
||||
fun `in`(column: KProperty1<T,*>, coll: Collection<*>?): Children {
|
||||
return this.`in`(true, column, coll)
|
||||
}
|
||||
|
||||
fun `in`(condition: Boolean, column: KProperty1<T,*>, coll: Collection<*>?): Children
|
||||
fun `in`(column: KProperty1<T,*>, vararg values: Any?): Children {
|
||||
return this.`in`(true, column, *values)
|
||||
}
|
||||
|
||||
fun `in`(condition: Boolean, column: KProperty1<T,*>, vararg values: Any?): Children {
|
||||
return this.`in`(condition, column, values.asList())
|
||||
}
|
||||
|
||||
fun notIn(column: KProperty1<T,*>, coll: Collection<*>?): Children {
|
||||
return this.notIn(true, column, coll)
|
||||
}
|
||||
|
||||
fun notIn(condition: Boolean, column: KProperty1<T,*>, coll: Collection<*>?): Children
|
||||
fun notIn(column: KProperty1<T,*>, vararg value: Any?): Children {
|
||||
return this.notIn(true, column, *value)
|
||||
}
|
||||
|
||||
fun notIn(condition: Boolean, column: KProperty1<T,*>, vararg values: Any?): Children {
|
||||
return this.notIn(condition, column, values.asList())
|
||||
}
|
||||
|
||||
fun inSql(column: KProperty1<T,*>, inValue: String?): Children {
|
||||
return this.inSql(true, column, inValue)
|
||||
}
|
||||
|
||||
fun inSql(condition: Boolean, column: KProperty1<T,*>, inValue: String?): Children
|
||||
fun notInSql(column: KProperty1<T,*>, inValue: String?): Children {
|
||||
return this.notInSql(true, column, inValue)
|
||||
}
|
||||
|
||||
fun notInSql(condition: Boolean, column: KProperty1<T,*>, inValue: String?): Children
|
||||
fun groupBy(column: KProperty1<T,*>): Children {
|
||||
return this.groupBy(true, column)
|
||||
}
|
||||
|
||||
fun groupBy(vararg columns: KProperty1<T,*>): Children {
|
||||
return this.groupBy(true, *columns)
|
||||
}
|
||||
|
||||
fun groupBy(condition: Boolean, vararg columns: KProperty1<T,*>): Children
|
||||
fun orderByAsc(column: KProperty1<T,*>): Children {
|
||||
return this.orderByAsc(true, column)
|
||||
}
|
||||
|
||||
fun orderByAsc(vararg columns: KProperty1<T,*>): Children {
|
||||
return this.orderByAsc(true, *columns)
|
||||
}
|
||||
|
||||
fun orderByAsc(condition: Boolean, vararg columns: KProperty1<T,*>): Children {
|
||||
return orderBy(condition, true, *columns)
|
||||
}
|
||||
|
||||
fun orderByDesc(column: KProperty1<T,*>): Children {
|
||||
return this.orderByDesc(true, column)
|
||||
}
|
||||
|
||||
fun orderByDesc(vararg columns: KProperty1<T,*>): Children {
|
||||
return this.orderByDesc(true, *columns)
|
||||
}
|
||||
|
||||
fun orderByDesc(condition: Boolean, vararg columns: KProperty1<T,*>): Children {
|
||||
return orderBy(condition, false, *columns)
|
||||
}
|
||||
|
||||
fun orderBy(condition: Boolean, isAsc: Boolean, vararg columns: KProperty1<T,*>): Children
|
||||
fun having(sqlHaving: String?, vararg params: Any?): Children {
|
||||
return this.having(true, sqlHaving, *params)
|
||||
}
|
||||
|
||||
fun having(condition: Boolean, sqlHaving: String?, vararg params: Any?): Children
|
||||
}
|
33
database/src/main/kotlin/cn/tursom/database/wrapper/Join.kt
Normal file
33
database/src/main/kotlin/cn/tursom/database/wrapper/Join.kt
Normal file
@ -0,0 +1,33 @@
|
||||
package cn.tursom.database.wrapper
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
interface Join<Children> : Serializable {
|
||||
fun or(condition: Boolean = true): Children
|
||||
|
||||
fun apply(applySql: String?, vararg value: Any?): Children {
|
||||
return this.apply(true, applySql, *value)
|
||||
}
|
||||
|
||||
fun apply(condition: Boolean, applySql: String?, vararg value: Any?): Children
|
||||
fun last(lastSql: String?): Children {
|
||||
return this.last(true, lastSql)
|
||||
}
|
||||
|
||||
fun last(condition: Boolean, lastSql: String?): Children
|
||||
fun comment(comment: String?): Children {
|
||||
return this.comment(true, comment)
|
||||
}
|
||||
|
||||
fun comment(condition: Boolean, comment: String?): Children
|
||||
fun exists(existsSql: String?): Children {
|
||||
return this.exists(true, existsSql)
|
||||
}
|
||||
|
||||
fun exists(condition: Boolean, existsSql: String?): Children
|
||||
fun notExists(notExistsSql: String?): Children {
|
||||
return this.notExists(true, notExistsSql)
|
||||
}
|
||||
|
||||
fun notExists(condition: Boolean, notExistsSql: String?): Children
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package cn.tursom.database.wrapper
|
||||
|
||||
import java.io.Serializable
|
||||
import java.util.function.Consumer
|
||||
|
||||
interface Nested<Param, Children> : Serializable {
|
||||
fun and(consumer: Consumer<Param>?): Children {
|
||||
return this.and(true, consumer)
|
||||
}
|
||||
|
||||
fun and(condition: Boolean, consumer: Consumer<Param>?): Children
|
||||
fun or(consumer: Consumer<Param>?): Children {
|
||||
return this.or(true, consumer)
|
||||
}
|
||||
|
||||
fun or(condition: Boolean, consumer: Consumer<Param>?): Children
|
||||
fun nested(consumer: Consumer<Param>?): Children {
|
||||
return this.nested(true, consumer)
|
||||
}
|
||||
|
||||
fun nested(condition: Boolean, consumer: Consumer<Param>?): Children
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package cn.tursom.database.wrapper
|
||||
|
||||
interface Wrapper<T>
|
||||
|
@ -1,14 +0,0 @@
|
||||
package cn.tursom.utils.cache
|
||||
|
||||
import cn.tursom.database.annotation.NotNull
|
||||
import cn.tursom.database.annotation.PrimaryKey
|
||||
|
||||
|
||||
data class StorageData(
|
||||
@PrimaryKey
|
||||
@NotNull
|
||||
val key: String,
|
||||
@NotNull
|
||||
val value: String,
|
||||
val cacheTime: Long = System.currentTimeMillis()
|
||||
)
|
7
microservices/build.gradle
Normal file
7
microservices/build.gradle
Normal file
@ -0,0 +1,7 @@
|
||||
dependencies {
|
||||
implementation project(":")
|
||||
implementation project(":AsyncSocket")
|
||||
|
||||
// kotlin 协程
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.tursom.microservices
|
||||
|
||||
import cn.tursom.socket.server.NioServer
|
||||
|
||||
class MicroserviceServer(val port: Int) {
|
||||
private val server = NioServer(port) {
|
||||
val content = ServiceContent()
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceContent {
|
||||
private val handler = ArrayList<Services<*, *>>()
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package cn.tursom.microservices
|
||||
|
||||
interface Services<T, R> {
|
||||
suspend fun handle(msg: T): R
|
||||
}
|
@ -6,4 +6,6 @@ include 'log'
|
||||
include 'json'
|
||||
include 'utils:yaml'
|
||||
include 'web:web-coroutine'
|
||||
include 'microservices'
|
||||
include 'database:database-mysql'
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
package cn.tursom.core
|
||||
|
||||
import sun.misc.Unsafe
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
import java.net.URLDecoder
|
||||
@ -115,108 +116,64 @@ fun String.simplifyPath(): String {
|
||||
return if (result.isNotEmpty()) result else "."
|
||||
}
|
||||
|
||||
fun ByteArray.md5(): ByteArray? {
|
||||
return try {
|
||||
//获取md5加密对象
|
||||
val instance = MessageDigest.getInstance("MD5")
|
||||
//加密,返回字节数组
|
||||
instance.digest(this)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
//获取md5加密对象
|
||||
val md5 by lazy { MessageDigest.getInstance("MD5")!! }
|
||||
|
||||
fun ByteArray.md5(): ByteArray {
|
||||
//加密,返回字节数组
|
||||
return md5.digest(this)
|
||||
}
|
||||
|
||||
fun String.md5(): String = toByteArray().md5().toHexString()
|
||||
|
||||
|
||||
//获取md5加密对象
|
||||
val sha256 by lazy { MessageDigest.getInstance("SHA-256")!! }
|
||||
|
||||
fun ByteArray.sha256(): ByteArray {
|
||||
//加密,返回字节数组
|
||||
return sha256.digest(this)
|
||||
}
|
||||
|
||||
fun String.sha256(): String = toByteArray().sha256().toHexString()
|
||||
|
||||
//获取sha加密对象
|
||||
val sha by lazy { MessageDigest.getInstance("SHA")!! }
|
||||
|
||||
fun ByteArray.sha(): ByteArray = sha.digest(this)
|
||||
|
||||
fun String.sha(): String = toByteArray().sha().toHexString()
|
||||
|
||||
//获取sha1加密对象
|
||||
val sha1 by lazy { MessageDigest.getInstance("SHA-1")!! }
|
||||
|
||||
fun ByteArray.sha1(): ByteArray = sha1.digest(this)
|
||||
|
||||
fun String.sha1(): String = toByteArray().sha1().toHexString()
|
||||
|
||||
//获取sha384加密对象
|
||||
val shA384 by lazy { MessageDigest.getInstance("SHA-384")!! }
|
||||
|
||||
fun ByteArray.sha384(): ByteArray = shA384.digest(this)
|
||||
|
||||
fun String.sha384(): String = toByteArray().sha384().toHexString()
|
||||
|
||||
//获取 sha-512 加密对象
|
||||
val sha512 by lazy { MessageDigest.getInstance("SHA-512")!! }
|
||||
|
||||
fun ByteArray.sha512(): ByteArray = sha512.digest(this)
|
||||
|
||||
fun String.sha512(): String = toByteArray().sha512().toHexString()
|
||||
|
||||
private val HEX_ARRAY = "0123456789abcdef".toCharArray()
|
||||
fun ByteArray.toHexString(): String {
|
||||
val hexChars = CharArray(size * 2)
|
||||
for (i in indices) {
|
||||
val b = this[i].toInt()
|
||||
hexChars[i shl 1] = HEX_ARRAY[b ushr 4 and 0x0F]
|
||||
hexChars[(i shl 1) + 1] = HEX_ARRAY[b and 0x0F]
|
||||
}
|
||||
}
|
||||
|
||||
fun String.md5(): String? {
|
||||
return toByteArray().md5()?.toHexString()
|
||||
}
|
||||
|
||||
fun ByteArray.sha256(): ByteArray? {
|
||||
return try {
|
||||
//获取md5加密对象
|
||||
val instance = MessageDigest.getInstance("SHA-256")
|
||||
//加密,返回字节数组
|
||||
instance.digest(this)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun String.sha256(): String? {
|
||||
return toByteArray().sha256()?.toHexString()
|
||||
}
|
||||
|
||||
fun ByteArray.sha(): ByteArray? {
|
||||
return try {
|
||||
//获取md5加密对象
|
||||
val instance = MessageDigest.getInstance("SHA")
|
||||
//对字符串加密,返回字节数组
|
||||
instance.digest(this)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun String.sha(): String? = toByteArray().sha()?.toHexString()
|
||||
|
||||
fun ByteArray.sha1(): ByteArray? {
|
||||
return try {
|
||||
//获取md5加密对象
|
||||
val instance = MessageDigest.getInstance("SHA-1")
|
||||
//对字符串加密,返回字节数组
|
||||
instance.digest(this)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun String.sha1(): String? = toByteArray().sha1()?.toHexString()
|
||||
|
||||
fun ByteArray.sha384(): ByteArray? {
|
||||
return try {
|
||||
//获取md5加密对象
|
||||
val instance = MessageDigest.getInstance("SHA-384")
|
||||
//对字符串加密,返回字节数组
|
||||
instance.digest(this)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun String.sha384(): String? = toByteArray().sha384()?.toHexString()
|
||||
|
||||
fun ByteArray.sha512(): ByteArray? {
|
||||
return try {
|
||||
//获取md5加密对象
|
||||
val instance = MessageDigest.getInstance("SHA-512")
|
||||
//对字符串加密,返回字节数组
|
||||
instance.digest(this)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun String.sha512(): String? = toByteArray().sha512()?.toHexString()
|
||||
|
||||
fun ByteArray.toHexString(): String? {
|
||||
val sb = StringBuilder()
|
||||
forEach {
|
||||
//获取低八位有效值+
|
||||
val i: Int = it.toInt() and 0xff
|
||||
//将整数转化为16进制
|
||||
var hexString = Integer.toHexString(i)
|
||||
if (hexString.length < 2) {
|
||||
//如果是一位的话,补0
|
||||
hexString = "0$hexString"
|
||||
}
|
||||
sb.append(hexString)
|
||||
}
|
||||
return sb.toString()
|
||||
return String(hexChars)
|
||||
}
|
||||
|
||||
fun ByteArray.toUTF8String() = String(this, Charsets.UTF_8)
|
||||
@ -258,3 +215,7 @@ operator fun Executor.invoke(action: () -> Unit) {
|
||||
}
|
||||
|
||||
operator fun Executor.invoke(action: Runnable) = this(action::run)
|
||||
|
||||
inline fun <reified T : Annotation> Field.getAnnotation(): T? = getAnnotation(T::class.java)
|
||||
|
||||
inline fun <reified T : Annotation> Class<*>.getAnnotation(): T? = getAnnotation(T::class.java)
|
||||
|
@ -5,6 +5,7 @@ enum class ElementTarget {
|
||||
}
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class DefaultTarget(val target: ElementTarget)
|
||||
|
||||
/**
|
||||
@ -14,6 +15,7 @@ annotation class DefaultTarget(val target: ElementTarget)
|
||||
* String
|
||||
*/
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Attribute
|
||||
|
||||
/**
|
||||
@ -23,9 +25,11 @@ annotation class Attribute
|
||||
* String
|
||||
*/
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ElementText
|
||||
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class SubElement
|
||||
|
||||
/**
|
||||
@ -36,15 +40,19 @@ annotation class SubElement
|
||||
* element 为根节点
|
||||
*/
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Constructor(val constructor: String)
|
||||
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class FieldName(val name: String)
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ElementName(val name: String)
|
||||
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class CompressionXml
|
||||
|
||||
/**
|
||||
@ -81,13 +89,16 @@ annotation class CompressionXml
|
||||
* ): Any
|
||||
*/
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ToXml(val callback: String)
|
||||
|
||||
/**
|
||||
* 数组所有的元素都同名,在同一个父节点下
|
||||
*/
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Vararg
|
||||
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Ignore
|
||||
|
Loading…
Reference in New Issue
Block a user