开始重构database模块

This commit is contained in:
tursom 2020-02-14 00:07:07 +08:00
parent 9f47f1d70d
commit 5ffbe0e728
77 changed files with 1084 additions and 3591 deletions

View File

@ -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
}

View File

@ -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())
}
}
}

View File

@ -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
}
}
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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 ?: "")
}
}

View File

@ -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()
}
}
}

View File

@ -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()
}
}
}

View File

@ -1,5 +0,0 @@
package cn.tursom.database.async
import io.vertx.core.Vertx
val vertx = Vertx.vertx()

View File

@ -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()
}
}
}

View 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
}

View File

@ -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.
}
}

View File

@ -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.
}
}

View File

@ -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
}
}
}

View 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}"
}

View File

@ -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
}
}

View 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>
}

View File

@ -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
}
}
}

View File

@ -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)
}

View File

@ -1,6 +0,0 @@
package cn.tursom.database
interface SqlField<T> {
fun get(): T
val sqlValue: String
}

View File

@ -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)

View File

@ -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
/**
* MySQLHelperSQLite辅助使用类
* 实现创建表格查询插入和更新功能
*/
@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)
}
}

View File

@ -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
}
}

View File

@ -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 ?: "")
}
}

View File

@ -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("'", "''")}'"
}

View 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 = "&nbsp;"
const val HTML_AMP = "&amp"
const val HTML_QUOTE = "&quot;"
const val HTML_LT = "&lt;"
const val HTML_GT = "&gt;"
// ---------------------------------------------------------------- array
val EMPTY_ARRAY = arrayOfNulls<String>(0)
val BYTES_NEW_LINE = NEWLINE.toByteArray()
}

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.FIELD)
annotation class AutoIncrement

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.FIELD)
annotation class Check(val func: String)

View File

@ -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)

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.FIELD)
annotation class Default(val default: String)

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.FIELD)
annotation class ExtraAttribute(val attributes: String)

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION)
annotation class FieldName(val name: String)

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.CLASS, AnnotationTarget.FIELD)
annotation class FieldType(val name: String)

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.FIELD, AnnotationTarget.CLASS)
annotation class ForeignKey(val target: String = "")

View File

@ -1,9 +0,0 @@
package cn.tursom.database.annotation
/**
* callback interface :
* getter(): Any?
*/
@MustBeDocumented
@Target(AnnotationTarget.FIELD)
annotation class Getter(val getter: String)

View File

@ -1,4 +0,0 @@
package cn.tursom.database.annotation
@Target(AnnotationTarget.FIELD)
annotation class Ignore

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.FIELD)
annotation class NotNull

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.FIELD)
annotation class PrimaryKey

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.CLASS)
annotation class StringField

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.CLASS)
annotation class TableName(val name: String)

View File

@ -1,8 +0,0 @@
package cn.tursom.database.annotation
/**
* only for string
*/
@MustBeDocumented
@Target(AnnotationTarget.FIELD)
annotation class TextLength(val length: Int)

View File

@ -1,5 +0,0 @@
package cn.tursom.database.annotation
@MustBeDocumented
@Target(AnnotationTarget.FIELD)
annotation class Unique

View File

@ -0,0 +1,6 @@
package cn.tursom.database.annotations
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.VALUE_PARAMETER)
annotation class Param(val value: String)

View File

@ -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
}

View File

@ -1,5 +0,0 @@
package cn.tursom.database.clauses
interface Clause {
val sqlStr: String
}

View File

@ -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()

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"
}
}

View File

@ -1,6 +0,0 @@
package cn.tursom.database.clauses
class NotClause(clause: Clause) : Clause {
override val sqlStr = "(NOT ${clause.sqlStr})"
override fun toString() = sqlStr
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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
/**
* MySQLHelperSQLite辅助使用类
* 实现创建表格查询插入和更新功能
*/
@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
}
}
}
}

View File

@ -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
/**
* MySQLHelperSQLite辅助使用类
* 实现创建表格查询插入和更新功能
*/
@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()
}
}
}

View File

@ -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>

View File

@ -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
}

View 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
}

View 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
}

View File

@ -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
}

View File

@ -0,0 +1,4 @@
package cn.tursom.database.wrapper
interface Wrapper<T>

View File

@ -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()
)

View File

@ -0,0 +1,7 @@
dependencies {
implementation project(":")
implementation project(":AsyncSocket")
// kotlin
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
}

View File

@ -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<*, *>>()
}

View File

@ -0,0 +1,5 @@
package cn.tursom.microservices
interface Services<T, R> {
suspend fun handle(msg: T): R
}

View File

@ -6,4 +6,6 @@ include 'log'
include 'json'
include 'utils:yaml'
include 'web:web-coroutine'
include 'microservices'
include 'database:database-mysql'

View File

@ -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)

View File

@ -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