From 655aafdb2723f7c50acc3987cdb9c63f8d68dd8c Mon Sep 17 00:00:00 2001 From: tursom Date: Wed, 11 Mar 2020 22:44:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8D=8F=E7=A8=8Bmongo?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/tursom/database/MysqlSqlHelper.kt | 172 ++++++++++-- .../cn/tursom/database/MysqlSqlTemplate.kt | 18 ++ .../tursom/database/wrapper/MysqlWrapper.kt | 135 --------- database/mongodb/mongodb-async/build.gradle | 11 + .../mongodb/async/AbstractSubscriber.kt | 17 ++ .../mongodb/async/AsyncMongoOperator.kt | 198 +++++++++++++ .../mongodb/async/AsyncMongoTemplate.kt | 57 ++++ .../kotlin/cn/tursom/mongodb/BsonFactory.kt | 14 + .../cn/tursom/mongodb/BsonFactoryImpl.kt | 23 ++ .../kotlin/cn/tursom/mongodb/MongoOperator.kt | 31 +- .../kotlin/cn/tursom/database/Constants.kt | 35 +-- .../main/kotlin/cn/tursom/database/Mapper.kt | 15 +- .../kotlin/cn/tursom/database/ResultSetMap.kt | 17 ++ .../kotlin/cn/tursom/database/SqlAdapter.kt | 22 ++ .../kotlin/cn/tursom/database/SqlHelper.kt | 63 ++--- .../kotlin/cn/tursom/database/SqlTemplate.kt | 5 + .../kotlin/cn/tursom/database/SqlUtils.kt | 66 +++++ .../cn/tursom/database/annotations/Delete.kt | 6 + .../cn/tursom/database/annotations/Insert.kt | 6 + .../cn/tursom/database/annotations/Param.kt | 3 +- .../cn/tursom/database/annotations/Select.kt | 7 + .../tursom/database/annotations/TableField.kt | 6 + .../tursom/database/annotations/TableName.kt | 6 + .../cn/tursom/database/annotations/Update.kt | 7 + .../database/wrapper/AbstractWrapper.kt | 265 +++++++++++++++++- .../cn/tursom/database/wrapper/Compare.kt | 101 +++---- .../kotlin/cn/tursom/database/wrapper/Func.kt | 215 +++++++++----- .../tursom/database/wrapper/IUpdateWrapper.kt | 5 + .../kotlin/cn/tursom/database/wrapper/Join.kt | 36 +-- .../cn/tursom/database/wrapper/Nested.kt | 22 -- .../cn/tursom/database/wrapper/Query.kt | 27 ++ .../tursom/database/wrapper/QueryWrapper.kt | 14 + .../cn/tursom/database/wrapper/Update.kt | 14 + .../tursom/database/wrapper/UpdateWrapper.kt | 25 ++ .../cn/tursom/database/wrapper/Wrapper.kt | 8 +- settings.gradle | 1 + src/main/kotlin/cn/tursom/core/Disposable.kt | 19 ++ .../kotlin/cn/tursom/utils/AsyncIterator.kt | 20 ++ .../src/main/kotlin/cn/tursom/utils/Tools.kt | 6 +- 39 files changed, 1261 insertions(+), 457 deletions(-) create mode 100644 database/database-mysql/src/main/kotlin/cn/tursom/database/MysqlSqlTemplate.kt delete mode 100644 database/database-mysql/src/main/kotlin/cn/tursom/database/wrapper/MysqlWrapper.kt create mode 100644 database/mongodb/mongodb-async/build.gradle create mode 100644 database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AbstractSubscriber.kt create mode 100644 database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AsyncMongoOperator.kt create mode 100644 database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AsyncMongoTemplate.kt create mode 100644 database/mongodb/src/main/kotlin/cn/tursom/mongodb/BsonFactory.kt create mode 100644 database/mongodb/src/main/kotlin/cn/tursom/mongodb/BsonFactoryImpl.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/ResultSetMap.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/SqlAdapter.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/SqlTemplate.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/SqlUtils.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/annotations/Delete.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/annotations/Insert.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/annotations/Select.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/annotations/TableField.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/annotations/TableName.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/annotations/Update.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/wrapper/IUpdateWrapper.kt delete mode 100644 database/src/main/kotlin/cn/tursom/database/wrapper/Nested.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/wrapper/Query.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/wrapper/QueryWrapper.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/wrapper/Update.kt create mode 100644 database/src/main/kotlin/cn/tursom/database/wrapper/UpdateWrapper.kt create mode 100644 src/main/kotlin/cn/tursom/core/Disposable.kt create mode 100644 utils/src/main/kotlin/cn/tursom/utils/AsyncIterator.kt diff --git a/database/database-mysql/src/main/kotlin/cn/tursom/database/MysqlSqlHelper.kt b/database/database-mysql/src/main/kotlin/cn/tursom/database/MysqlSqlHelper.kt index 14a90bd..687d80d 100644 --- a/database/database-mysql/src/main/kotlin/cn/tursom/database/MysqlSqlHelper.kt +++ b/database/database-mysql/src/main/kotlin/cn/tursom/database/MysqlSqlHelper.kt @@ -1,82 +1,194 @@ package cn.tursom.database -import cn.tursom.database.wrapper.MysqlWrapper +import cn.tursom.database.wrapper.IUpdateWrapper +import cn.tursom.database.wrapper.Query +import cn.tursom.database.wrapper.Wrapper import java.io.Serializable +import java.sql.PreparedStatement +import java.sql.Types +import java.util.* +import javax.sql.DataSource + +class MysqlSqlHelper( + val clazz: Class, + var dataSource: DataSource +) : SqlHelper { + private val table = SqlUtils { clazz.tableName } + private val columns = clazz.declaredFields.map { + SqlUtils { it.tableField } to it + } + private val insert = run { + val sql = StringBuilder("INSERT INTO $table (") + columns.forEach { + sql.append(it.first) + sql.append(',') + } + sql.deleteCharAt(sql.length - 1) + sql.append(") VALUES ") + sql.toString() + } + private val valuesTemplate = run { + val sql = StringBuilder("(") + repeat(columns.size) { + sql.append("?,") + } + sql.deleteCharAt(sql.length - 1) + sql.append(")") + sql.toString() + } + private val insertOne = "$insert$valuesTemplate;" + + private val selects = clazz.declaredFields + .map { SqlUtils { it.selectField } } + .let { + val sb = StringBuilder() + it.forEach { field -> + sb.append(field) + sb.append(',') + } + sb.deleteCharAt(sb.lastIndex) + sb.toString() + } -class MysqlSqlHelper : SqlHelper> { override fun save(entity: T): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + val conn = dataSource.connection + val statement = conn.prepareStatement(insertOne) + columns.forEachIndexed { index, (_, field) -> + setValue(statement, index, field.get(entity)) + } + return statement.executeUpdate() != 0 } - override fun saveBatch(entityList: Collection, batchSize: Int): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun saveBatch(entityList: Collection): Int { + if (entityList.isEmpty()) { + return 0 + } + val sqlBuilder = StringBuilder(insert) + repeat(entityList.size) { + sqlBuilder.append(valuesTemplate) + sqlBuilder.append(',') + } + sqlBuilder.deleteCharAt(sqlBuilder.lastIndex) + sqlBuilder.append(';') + val sql = sqlBuilder.toString() + + val conn = dataSource.connection + val statement = conn.prepareStatement(sql) + try { + var index = 0 + entityList.forEach { + columns.forEach { (_, field) -> + setValue(statement, index, field.get(it)) + index++ + } + } + return statement.executeUpdate() + } finally { + statement.close() + conn.close() + } } - override fun saveOrUpdateBatch(entityList: Collection, batchSize: Int): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } + //override fun saveOrUpdateBatch(entityList: Collection, batchSize: Int): Boolean { + // TODO("Not yet implemented") + //} override fun removeById(id: Serializable?): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") } override fun removeByMap(columnMap: Map): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") } - override fun remove(queryWrapper: MysqlWrapper): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun remove(queryWrapper: Wrapper): Boolean { + TODO("Not yet implemented") } override fun removeByIds(idList: Collection): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") } override fun updateById(entity: T): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") } - override fun update(entity: T?, updateWrapper: MysqlWrapper): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun update(entity: T?, updateWrapper: IUpdateWrapper): Boolean { + TODO("Not yet implemented") } override fun updateBatchById(entityList: Collection, batchSize: Int): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") } override fun saveOrUpdate(entity: T?): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") } override fun getById(id: Serializable?): T? { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") } override fun listByIds(idList: Collection): Collection { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") } override fun listByMap(columnMap: Map): Collection { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") } - override fun getOne(queryWrapper: MysqlWrapper, throwEx: Boolean): T? { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun getOne(queryWrapper: Wrapper, throwEx: Boolean): T? { + TODO("Not yet implemented") } - override fun getMap(queryWrapper: MysqlWrapper): Map { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun getMap(queryWrapper: Wrapper): Map { + TODO("Not yet implemented") } - override fun count(queryWrapper: MysqlWrapper): Int { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun count(queryWrapper: Wrapper): Int { + TODO("Not yet implemented") } - override fun list(queryWrapper: MysqlWrapper): List { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun list(queryWrapper: Wrapper): List { + val select = if (queryWrapper !is Query<*, *>) { + selects + } else { + val select = queryWrapper.sqlSelect + if (select.isEmpty()) { + selects + } else { + select + } + } + + val (where, whereParams) = queryWrapper.where + val sql = "SELECT $select FROM $table WHERE (${where});" + dataSource.connection.use { conn -> + conn.prepareStatement(sql).use { statement -> + var index = 0 + whereParams.forEach { + setValue(statement, index, it) + index++ + } + val query = statement.executeQuery() + val adapter = SqlAdapter(clazz) + query.use { adapter.adapt(query) } + return adapter + } + } } override fun getBaseMapper(): Mapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + TODO("Not yet implemented") + } + + companion object { + fun setValue(statement: PreparedStatement, index: Int, value: Any?) { + when (value) { + null -> statement.setNull(index + 1, Types.NULL) + is Date -> statement.setDate(index + 1, java.sql.Date(value.time)) + else -> statement.setObject(index + 1, value) + } + } } } \ No newline at end of file diff --git a/database/database-mysql/src/main/kotlin/cn/tursom/database/MysqlSqlTemplate.kt b/database/database-mysql/src/main/kotlin/cn/tursom/database/MysqlSqlTemplate.kt new file mode 100644 index 0000000..0a402d5 --- /dev/null +++ b/database/database-mysql/src/main/kotlin/cn/tursom/database/MysqlSqlTemplate.kt @@ -0,0 +1,18 @@ +package cn.tursom.database + +import javax.sql.DataSource + +class MysqlSqlTemplate( + private val dataSource: DataSource +) : SqlTemplate { + + override fun getHelper(clazz: Class): MysqlSqlHelper { + TODO("Not yet implemented") + } + + companion object { + init { + Class.forName("com.mysql.cj.jdbc.Driver"); + } + } +} \ No newline at end of file diff --git a/database/database-mysql/src/main/kotlin/cn/tursom/database/wrapper/MysqlWrapper.kt b/database/database-mysql/src/main/kotlin/cn/tursom/database/wrapper/MysqlWrapper.kt deleted file mode 100644 index 5232547..0000000 --- a/database/database-mysql/src/main/kotlin/cn/tursom/database/wrapper/MysqlWrapper.kt +++ /dev/null @@ -1,135 +0,0 @@ -package cn.tursom.database.wrapper - -import java.util.function.BiPredicate -import java.util.function.Consumer -import kotlin.reflect.KProperty1 - -open class MysqlWrapper : AbstractWrapper> { - override fun allEq(condition: Boolean, params: Map, V>?, null2IsNull: Boolean): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun allEq(condition: Boolean, filter: BiPredicate, V>?, params: Map, V>?, null2IsNull: Boolean): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun eq(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun ne(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun gt(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun ge(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun lt(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun le(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun between(condition: Boolean, column: KProperty1, val1: Any?, val2: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun notBetween(condition: Boolean, column: KProperty1, val1: Any?, val2: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun like(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun notLike(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun likeLeft(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun likeRight(condition: Boolean, column: KProperty1, `val`: Any?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun and(condition: Boolean, consumer: Consumer>?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun or(condition: Boolean, consumer: Consumer>?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun or(condition: Boolean): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun nested(condition: Boolean, consumer: Consumer>?): MysqlWrapper { - 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 { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun last(condition: Boolean, lastSql: String?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun comment(condition: Boolean, comment: String?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun exists(condition: Boolean, existsSql: String?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun notExists(condition: Boolean, notExistsSql: String?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun isNull(condition: Boolean, column: KProperty1): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun isNotNull(condition: Boolean, column: KProperty1): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun `in`(condition: Boolean, column: KProperty1, coll: Collection<*>?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun notIn(condition: Boolean, column: KProperty1, coll: Collection<*>?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun inSql(condition: Boolean, column: KProperty1, inValue: String?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun notInSql(condition: Boolean, column: KProperty1, inValue: String?): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun groupBy(condition: Boolean, vararg columns: KProperty1): MysqlWrapper { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun orderBy(condition: Boolean, isAsc: Boolean, vararg columns: KProperty1): MysqlWrapper { - 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 { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } -} \ No newline at end of file diff --git a/database/mongodb/mongodb-async/build.gradle b/database/mongodb/mongodb-async/build.gradle new file mode 100644 index 0000000..2bfc4d3 --- /dev/null +++ b/database/mongodb/mongodb-async/build.gradle @@ -0,0 +1,11 @@ + +dependencies { + implementation project(":") + implementation project(":utils") + implementation project(":database:mongodb") + // https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-reactivestreams + compile group: 'org.mongodb', name: 'mongodb-driver-reactivestreams', version: '4.0.0' + api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion + // kotlin 协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' +} \ No newline at end of file diff --git a/database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AbstractSubscriber.kt b/database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AbstractSubscriber.kt new file mode 100644 index 0000000..3105e1e --- /dev/null +++ b/database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AbstractSubscriber.kt @@ -0,0 +1,17 @@ +package cn.tursom.mongodb.async + +import org.reactivestreams.Subscriber +import org.reactivestreams.Subscription + +abstract class AbstractSubscriber : Subscriber { + var compete = false + lateinit var subscription: Subscription + + override fun onComplete() { + compete = true + } + + override fun onSubscribe(s: Subscription) { + subscription = s + } +} \ No newline at end of file diff --git a/database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AsyncMongoOperator.kt b/database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AsyncMongoOperator.kt new file mode 100644 index 0000000..9b54ac9 --- /dev/null +++ b/database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AsyncMongoOperator.kt @@ -0,0 +1,198 @@ +package cn.tursom.mongodb.async + +import cn.tursom.core.Disposable +import cn.tursom.mongodb.BsonFactoryImpl +import cn.tursom.mongodb.MongoUtil +import cn.tursom.mongodb.Update +import cn.tursom.utils.AsyncIterator +import cn.tursom.utils.forEach +import com.mongodb.MongoClientSettings +import com.mongodb.ServerAddress +import com.mongodb.client.model.DeleteOptions +import com.mongodb.client.model.InsertManyOptions +import com.mongodb.client.model.InsertOneOptions +import com.mongodb.client.model.UpdateOptions +import com.mongodb.client.result.InsertManyResult +import com.mongodb.client.result.InsertOneResult +import com.mongodb.client.result.UpdateResult +import com.mongodb.reactivestreams.client.MongoClients +import com.mongodb.reactivestreams.client.MongoCollection +import com.mongodb.reactivestreams.client.MongoDatabase +import kotlinx.coroutines.runBlocking +import org.bson.Document +import org.bson.conversions.Bson +import org.reactivestreams.Publisher +import org.reactivestreams.Subscriber +import org.reactivestreams.Subscription +import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import kotlin.reflect.KProperty1 + +class AsyncMongoOperator( + @Suppress("MemberVisibilityCanBePrivate") val collection: MongoCollection, + clazz: Class +) : MongoCollection by collection, BsonFactoryImpl(clazz) { + constructor(clazz: Class, db: MongoDatabase) : this(db.getCollection(MongoUtil.collectionName(clazz)), clazz) + + suspend fun save(entity: T, options: InsertOneOptions = InsertOneOptions()) { + val publisher = collection.insertOne(convertToBson(entity), options) + suspendCoroutine { cont -> + publisher.subscribe(object : Subscriber { + override fun onComplete() = cont.resume(Unit) + override fun onSubscribe(s: Subscription?) {} + override fun onNext(t: InsertOneResult?) {} + override fun onError(t: Throwable) = cont.resumeWithException(t) + }) + } + } + + + suspend fun save(entities: Collection, options: InsertManyOptions = InsertManyOptions()) { + val publisher = collection.insertMany(entities.map { convertToBson(it) }, options) + suspendCoroutine { cont -> + publisher.subscribe(object : Subscriber { + override fun onComplete() = cont.resume(Unit) + override fun onSubscribe(s: Subscription?) {} + override fun onNext(t: InsertManyResult) {} + override fun onError(t: Throwable) = cont.resumeWithException(t) + }) + } + } + + suspend fun update(update: Bson, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult { + val publisher = collection.updateOne(where, update, options) + return suspendCoroutine { cont -> + publisher.subscribe(object : Subscriber { + override fun onComplete() {} + override fun onSubscribe(s: Subscription) {} + override fun onNext(t: UpdateResult) = cont.resume(t) + override fun onError(t: Throwable) = cont.resumeWithException(t) + }) + } + } + + suspend fun update(entity: T, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult { + return update(convertToBson(entity), where, options) + } + + @Suppress("SpellCheckingInspection") + suspend fun upsert(entity: T, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult { + return update(entity, where, options.upsert(true)) + } + + @Suppress("SpellCheckingInspection") + suspend fun upsert(update: Bson, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult { + return update(update, where, options.upsert(true)) + } + + suspend fun add(field: KProperty1, value: Number, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult { + return upsert( + Update { field inc value }, + where, options + ) + } + + suspend fun inc(field: KProperty1, where: Bson): UpdateResult { + return add(field, 1, where) + } + + suspend fun getOne(where: Bson? = null): T? { + val publisher = if (where == null) find() else find(where) + return suspendCoroutine { cont -> + publisher.subscribe(object : AbstractSubscriber() { + var resumed = false + override fun onComplete() { + super.onComplete() + if (!resumed) cont.resume(null) + } + + override fun onSubscribe(s: Subscription) = s.request(1) + override fun onNext(t: Document) = cont.resume(parse(t)) + override fun onError(t: Throwable) = cont.resumeWithException(t) + }) + } + } + + suspend fun list(where: Bson? = null): List { + val publisher = if (where == null) find() else find(where) + val resultList = ArrayList() + suspendCoroutine { cont -> + publisher.subscribe(object : AbstractSubscriber() { + override fun onComplete() { + super.onComplete() + cont.resume(Unit) + } + + override fun onSubscribe(s: Subscription) = s.request(Int.MAX_VALUE.toLong()) + override fun onNext(t: Document) { + resultList.add(parse(t)) + } + + override fun onError(t: Throwable) = cont.resumeWithException(t) + }) + } + return resultList + } + + fun get(where: Bson? = null, bufSize: Int = 32): AsyncIterator { + val find = if (where == null) find() else find(where) + return iterator(find, bufSize) + } + + fun aggregate(vararg pipeline: Bson, bufSize: Int = 32) = iterator(aggregate(pipeline.asList()), bufSize) + + private fun iterator(publisher: Publisher, bufSize: Int = 32): AsyncIterator { + val subscriber = object : AbstractSubscriber(), AsyncIterator { + private var cont = Disposable>() + private var notify = Disposable>() + private val cache = ConcurrentLinkedQueue() + + override fun onComplete() { + super.onComplete() + cont.get()?.resumeWithException(Exception()) + notify.get()?.resume(Unit) + } + + override fun onNext(t: Document) { + val entity = parse(t) + cont.get()?.resume(entity) ?: cache.add(entity) + notify.get()?.resume(Unit) + } + + override fun onError(t: Throwable) { + cont.get()?.resumeWithException(t) ?: t.printStackTrace() + } + + override suspend fun next(): T { + return cache.poll() ?: suspendCoroutine { cont -> + this.cont.set(cont) + subscription.request(bufSize.toLong()) + } + } + + override suspend fun hasNext(): Boolean = if (cache.isEmpty()) { + suspendCoroutine { + notify.set(it) + subscription.request(bufSize.toLong()) + } + !compete + } else { + !compete + } + } + publisher.subscribe(subscriber) + return subscriber + } + + fun delete(entity: T, options: DeleteOptions = DeleteOptions()) { + deleteOne(convertToBson(entity), options) + } + + suspend fun saveIfNotExists(entity: T) { + val document = convertToBson(entity) + upsert(document, document) + } +} \ No newline at end of file diff --git a/database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AsyncMongoTemplate.kt b/database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AsyncMongoTemplate.kt new file mode 100644 index 0000000..4a600da --- /dev/null +++ b/database/mongodb/mongodb-async/src/main/kotlin/cn/tursom/mongodb/async/AsyncMongoTemplate.kt @@ -0,0 +1,57 @@ +package cn.tursom.mongodb.async + +import cn.tursom.core.cast +import com.mongodb.MongoClientSettings +import com.mongodb.ServerAddress +import com.mongodb.client.model.InsertManyOptions +import com.mongodb.client.model.InsertOneOptions +import com.mongodb.client.model.UpdateOptions +import com.mongodb.reactivestreams.client.MongoClient +import com.mongodb.reactivestreams.client.MongoClients +import org.bson.conversions.Bson +import java.util.concurrent.ConcurrentHashMap + +class AsyncMongoTemplate( + val mongoClient: MongoClient, + db: String +) { + constructor(db: String, vararg servers: ServerAddress) : this(MongoClientSettings.builder().let { + it.applyToClusterSettings { builder -> + builder.hosts(servers.asList()) + } + MongoClients.create(it.build()) + }, db) + + constructor(host: String, port: Int, db: String) : this(db, ServerAddress(host, port)) + + val db = mongoClient.getDatabase(db) + private val operatorMap = ConcurrentHashMap, AsyncMongoOperator<*>>() + + + suspend fun save(entity: T, options: InsertOneOptions = InsertOneOptions()) { + getCollection(entity.javaClass).save(entity, options) + } + + suspend inline fun save(entities: Collection, options: InsertManyOptions = InsertManyOptions()) { + getCollection().save(entities, options) + } + + suspend fun update(entity: T, where: Bson, options: UpdateOptions = UpdateOptions()) { + getCollection(entity.javaClass).update(entity, where, options) + } + + @Suppress("SpellCheckingInspection") + suspend fun upsert(entity: T, where: Bson, options: UpdateOptions = UpdateOptions()) { + getCollection(entity.javaClass).upsert(entity, where, options) + } + + inline fun getCollection(): AsyncMongoOperator = getCollection(T::class.java) + + fun getCollection(clazz: Class): AsyncMongoOperator { + return operatorMap[clazz]?.cast() ?: run { + val operator = AsyncMongoOperator(clazz, db) + operatorMap[clazz] = operator + operator + } + } +} \ No newline at end of file diff --git a/database/mongodb/src/main/kotlin/cn/tursom/mongodb/BsonFactory.kt b/database/mongodb/src/main/kotlin/cn/tursom/mongodb/BsonFactory.kt new file mode 100644 index 0000000..4735015 --- /dev/null +++ b/database/mongodb/src/main/kotlin/cn/tursom/mongodb/BsonFactory.kt @@ -0,0 +1,14 @@ +package cn.tursom.mongodb + +import cn.tursom.core.Parser +import org.bson.Document + +interface BsonFactory { + val clazz: Class + + fun parse(document: Document) = Parser.parse(document, clazz)!! + + fun convertToBson(entity: Any): Document + + //fun convertToEntity(bson: Document): T +} \ No newline at end of file diff --git a/database/mongodb/src/main/kotlin/cn/tursom/mongodb/BsonFactoryImpl.kt b/database/mongodb/src/main/kotlin/cn/tursom/mongodb/BsonFactoryImpl.kt new file mode 100644 index 0000000..a77dac6 --- /dev/null +++ b/database/mongodb/src/main/kotlin/cn/tursom/mongodb/BsonFactoryImpl.kt @@ -0,0 +1,23 @@ +package cn.tursom.mongodb + +import cn.tursom.core.Unsafe +import cn.tursom.core.cast +import cn.tursom.core.isStatic +import cn.tursom.core.isTransient +import cn.tursom.mongodb.annotation.Ignore +import org.bson.Document + +open class BsonFactoryImpl(final override val clazz: Class) : BsonFactory { + private val fields = clazz.declaredFields.filter { + it.isAccessible = true + !it.isStatic() && !it.isTransient() && it.getAnnotation(Ignore::class.java) == null + } + + override fun convertToBson(entity: Any): Document { + val bson = Document() + fields.forEach { + MongoUtil.injectValue(bson, it.get(entity) ?: return@forEach, it) + } + return bson + } +} \ No newline at end of file diff --git a/database/mongodb/src/main/kotlin/cn/tursom/mongodb/MongoOperator.kt b/database/mongodb/src/main/kotlin/cn/tursom/mongodb/MongoOperator.kt index 50244b8..b8b21d6 100644 --- a/database/mongodb/src/main/kotlin/cn/tursom/mongodb/MongoOperator.kt +++ b/database/mongodb/src/main/kotlin/cn/tursom/mongodb/MongoOperator.kt @@ -16,14 +16,12 @@ import org.bson.conversions.Bson import kotlin.reflect.KProperty1 @Suppress("MemberVisibilityCanBePrivate", "CanBeParameter", "unused") -class MongoOperator( +open class MongoOperator( val collection: MongoCollection, - val clazz: Class -) : MongoCollection by collection { + clazz: Class +) : MongoCollection by collection, BsonFactoryImpl(clazz) { constructor(clazz: Class, database: MongoDatabase) : this(database.getCollection(MongoUtil.collectionName(clazz)), clazz) - fun parse(document: Document) = Parser.parse(document, clazz) - private val fields = clazz.declaredFields.filter { it.isAccessible = true !it.isStatic() && !it.isTransient() && it.getAnnotation(Ignore::class.java) == null @@ -120,26 +118,5 @@ class MongoOperator( val document = convertToBson(entity) upsert(document, document) } +} - fun convertToBson(entity: Any): Document { - val bson = Document() - fields.forEach { - MongoUtil.injectValue(bson, it.get(entity) ?: return@forEach, it) - } - return bson - } - - fun convertToEntity(bson: Document): T { - val entity = try { - clazz.newInstance() - } catch (e: Exception) { - Unsafe.unsafe.allocateInstance(clazz) - }.cast() - fields.forEach { - val value = bson[MongoUtil.fieldName(it)] ?: return@forEach - MongoUtil.injectValue(bson, value, it) - } - return entity - } - -} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/Constants.kt b/database/src/main/kotlin/cn/tursom/database/Constants.kt index 5596a1e..8c83de8 100644 --- a/database/src/main/kotlin/cn/tursom/database/Constants.kt +++ b/database/src/main/kotlin/cn/tursom/database/Constants.kt @@ -13,7 +13,7 @@ object Constants { /** * project name */ - const val MYBATIS_PLUS = "mybatis-plus" + const val TABLE = "table" /** * MD5 */ @@ -34,38 +34,7 @@ object Constants { * 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 + const val WRAPPER_WHERE = WRAPPER_DOT + "where" /** * UpdateWrapper 类的属性 sqlSet */ diff --git a/database/src/main/kotlin/cn/tursom/database/Mapper.kt b/database/src/main/kotlin/cn/tursom/database/Mapper.kt index 3c6c1a9..1d1c046 100644 --- a/database/src/main/kotlin/cn/tursom/database/Mapper.kt +++ b/database/src/main/kotlin/cn/tursom/database/Mapper.kt @@ -1,5 +1,7 @@ package cn.tursom.database +import cn.tursom.database.annotations.Delete +import cn.tursom.database.annotations.Insert import cn.tursom.database.annotations.Param import cn.tursom.database.wrapper.Wrapper import java.io.Serializable @@ -18,20 +20,23 @@ interface Mapper { * * @param entity 实体对象 */ - fun insert(entity: T): Int + @Insert("insert into \${${Constants.TABLE}} #{${Constants.ENTITY}}") + fun insert(@Param(Constants.ENTITY) entity: T): Int /** * 根据 ID 删除 * * @param id 主键ID */ - fun deleteById(id: Serializable): Int + @Delete("delete from \${${Constants.TABLE}} where id=#{id}") + fun deleteById(@Param("id") id: Serializable): Int /** * 根据 columnMap 条件,删除记录 * * @param columnMap 表字段 map 对象 */ + @Delete("delete from \${${Constants.TABLE}} where #{${Constants.COLUMN_MAP}}") fun deleteByMap(@Param(Constants.COLUMN_MAP) columnMap: Map): Int /** @@ -39,6 +44,7 @@ interface Mapper { * * @param wrapper 实体对象封装操作类(可以为 null) */ + @Delete("delete from \${${Constants.TABLE}} where #{${Constants.WRAPPER_WHERE}}") fun delete(@Param(Constants.WRAPPER) wrapper: Wrapper): Int /** @@ -120,4 +126,9 @@ interface Mapper { * @param queryWrapper 实体对象封装操作类(可以为 null) */ fun selectObjs(@Param(Constants.WRAPPER) queryWrapper: Wrapper): List + + companion object { + fun instance(clazz: Class>) { + } + } } \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/ResultSetMap.kt b/database/src/main/kotlin/cn/tursom/database/ResultSetMap.kt new file mode 100644 index 0000000..c30da0c --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/ResultSetMap.kt @@ -0,0 +1,17 @@ +package cn.tursom.database + +import java.sql.ResultSet + +class ResultSetMap( + val resultSet: ResultSet +) : Map { + override val entries: Set> get() = TODO("Not yet implemented") + override val keys: Set get() = TODO("Not yet implemented") + override val size: Int get() = resultSet.fetchSize + override val values: Collection get() = TODO("Not yet implemented") + override fun containsKey(key: String): Boolean = TODO("Not yet implemented") + override fun containsValue(value: Any?): Boolean = TODO("Not yet implemented") + override fun isEmpty(): Boolean = TODO("Not yet implemented") + + override fun get(key: String): Any? = resultSet.getObject(key) +} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/SqlAdapter.kt b/database/src/main/kotlin/cn/tursom/database/SqlAdapter.kt new file mode 100644 index 0000000..e4e3f07 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/SqlAdapter.kt @@ -0,0 +1,22 @@ +package cn.tursom.database + +import cn.tursom.core.Parser +import java.sql.ResultSet +import java.sql.SQLException + +open class SqlAdapter(private val clazz: Class) : ArrayList() { + open fun adapt(resultSet: ResultSet) { + clear() //清空已储存的数据 + val resultMap = ResultSetMap(resultSet) + // 遍历ResultSet + while (resultSet.next()) { + try { + add(Parser.parse(resultMap, clazz) ?: continue) + } catch (e: SQLException) { + // ignored + } catch (e: IllegalStateException) { + // ignored + } + } + } +} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/SqlHelper.kt b/database/src/main/kotlin/cn/tursom/database/SqlHelper.kt index b175a97..a40cf83 100644 --- a/database/src/main/kotlin/cn/tursom/database/SqlHelper.kt +++ b/database/src/main/kotlin/cn/tursom/database/SqlHelper.kt @@ -1,10 +1,11 @@ package cn.tursom.database +import cn.tursom.database.wrapper.IUpdateWrapper import cn.tursom.database.wrapper.Wrapper import java.io.Serializable @Suppress("unused") -interface SqlHelper> { +interface SqlHelper { /** * 插入一条记录(选择字段,策略插入) * @@ -17,34 +18,24 @@ interface SqlHelper> { * * @param entityList 实体对象集合 */ - fun saveBatch(entityList: Collection): Boolean { - return saveBatch(entityList, 1000) - } + fun saveBatch(entityList: Collection): Int - /** - * 插入(批量) - * - * @param entityList 实体对象集合 - * @param batchSize 插入批次数量 - */ - fun saveBatch(entityList: Collection, batchSize: Int): Boolean - - /** - * 批量修改插入 - * - * @param entityList 实体对象集合 - */ - fun saveOrUpdateBatch(entityList: Collection): Boolean { - return saveOrUpdateBatch(entityList, 1000) - } - - /** - * 批量修改插入 - * - * @param entityList 实体对象集合 - * @param batchSize 每次的数量 - */ - fun saveOrUpdateBatch(entityList: Collection, batchSize: Int): Boolean + ///** + // * 批量修改插入 + // * + // * @param entityList 实体对象集合 + // */ + //fun saveOrUpdateBatch(entityList: Collection): Boolean { + // return saveOrUpdateBatch(entityList, 1000) + //} + // + ///** + // * 批量修改插入 + // * + // * @param entityList 实体对象集合 + // * @param batchSize 每次的数量 + // */ + //fun saveOrUpdateBatch(entityList: Collection, batchSize: Int): Boolean /** * 根据 ID 删除 @@ -63,7 +54,7 @@ interface SqlHelper> { /** * 根据 entity 条件,删除记录 */ - fun remove(queryWrapper: W): Boolean + fun remove(queryWrapper: Wrapper): Boolean /** * 删除(根据ID 批量删除) @@ -85,14 +76,14 @@ interface SqlHelper> { * @param entity 实体对象 * @param updateWrapper 实体对象封装操作类 */ - fun update(entity: T?, updateWrapper: W): Boolean + fun update(entity: T?, updateWrapper: IUpdateWrapper): Boolean /** * 根据 UpdateWrapper 条件,更新记录 需要设置sqlset * * @param updateWrapper 实体对象封装操作类 */ - fun update(updateWrapper: W): Boolean { + fun update(updateWrapper: IUpdateWrapper): Boolean { return update(null, updateWrapper) } @@ -147,14 +138,14 @@ interface SqlHelper> { * @param queryWrapper 实体对象封装操作类 * @param throwEx 有多个 result 是否抛出异常 */ - fun getOne(queryWrapper: W, throwEx: Boolean = true): T? + fun getOne(queryWrapper: Wrapper, throwEx: Boolean = true): T? /** * 根据 Wrapper,查询一条记录 * * @param queryWrapper 实体对象封装操作类 */ - fun getMap(queryWrapper: W): Map + fun getMap(queryWrapper: Wrapper): Map ///** // * 根据 Wrapper,查询一条记录 @@ -169,14 +160,14 @@ interface SqlHelper> { * * @param queryWrapper 实体对象封装操作类 */ - fun count(queryWrapper: W): Int + fun count(queryWrapper: Wrapper): Int /** * 查询列表 * * @param queryWrapper 实体对象封装操作类 */ - fun list(queryWrapper: W): List + fun list(queryWrapper: Wrapper): List /** * 获取对应 entity 的 BaseMapper @@ -194,7 +185,7 @@ interface SqlHelper> { * * @param entity 实体对象 */ - fun saveOrUpdate(entity: T?, updateWrapper: W): Boolean { + fun saveOrUpdate(entity: T?, updateWrapper: IUpdateWrapper): Boolean { return update(entity, updateWrapper) || saveOrUpdate(entity) } } diff --git a/database/src/main/kotlin/cn/tursom/database/SqlTemplate.kt b/database/src/main/kotlin/cn/tursom/database/SqlTemplate.kt new file mode 100644 index 0000000..eb382c8 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/SqlTemplate.kt @@ -0,0 +1,5 @@ +package cn.tursom.database + +interface SqlTemplate { + fun getHelper(clazz: Class): SqlHelper +} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/SqlUtils.kt b/database/src/main/kotlin/cn/tursom/database/SqlUtils.kt new file mode 100644 index 0000000..f0efb94 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/SqlUtils.kt @@ -0,0 +1,66 @@ +package cn.tursom.database + +import cn.tursom.database.annotations.TableField +import cn.tursom.database.annotations.TableName +import java.lang.reflect.Field +import java.sql.Types +import kotlin.reflect.KClass +import kotlin.reflect.KProperty +import kotlin.reflect.full.findAnnotation +import kotlin.reflect.jvm.javaField + +@Suppress("MemberVisibilityCanBePrivate", "unused") +object SqlUtils { + inline operator fun invoke(action: SqlUtils.() -> T) = this.action() + + val String.sqlName: String + get() { + val sb = StringBuilder() + val iterator = iterator() + sb.append(iterator.nextChar().toLowerCase()) + iterator.forEach { + if (it.isUpperCase()) { + sb.append('_') + sb.append(it.toLowerCase()) + } else { + sb.append(it) + } + } + return sb.toString() + } + + val KProperty<*>.tableField: String + get() { + val tableField = findAnnotation() ?: javaField?.getAnnotation(TableField::class.java) + return tableField?.fieldName ?: name.sqlName + } + + val KProperty<*>.selectField: String + get() { + val tableField = findAnnotation() ?: javaField?.getAnnotation(TableField::class.java) + return if (tableField != null) { + "${tableField.fieldName} as ${name.sqlName}" + } else { + name.sqlName + } + } + + val Field.tableField: String + get() { + val tableField = getAnnotation(TableField::class.java) + return tableField?.fieldName ?: name.sqlName + } + + val Field.selectField: String + get() { + val tableField = getAnnotation(TableField::class.java) + return if (tableField != null) { + "${tableField.fieldName} as ${name.sqlName}" + } else { + name.sqlName + } + } + + val KClass<*>.tableName: String get() = findAnnotation()?.value ?: simpleName!!.sqlName + val Class<*>.tableName: String get() = getAnnotation(TableName::class.java)?.value ?: simpleName!!.sqlName +} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/annotations/Delete.kt b/database/src/main/kotlin/cn/tursom/database/annotations/Delete.kt new file mode 100644 index 0000000..8a26c8f --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/annotations/Delete.kt @@ -0,0 +1,6 @@ +package cn.tursom.database.annotations + +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class Delete(val sql: String) diff --git a/database/src/main/kotlin/cn/tursom/database/annotations/Insert.kt b/database/src/main/kotlin/cn/tursom/database/annotations/Insert.kt new file mode 100644 index 0000000..5ca5b7e --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/annotations/Insert.kt @@ -0,0 +1,6 @@ +package cn.tursom.database.annotations + +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class Insert(val sql: String) \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/annotations/Param.kt b/database/src/main/kotlin/cn/tursom/database/annotations/Param.kt index a40cf73..4b795c1 100644 --- a/database/src/main/kotlin/cn/tursom/database/annotations/Param.kt +++ b/database/src/main/kotlin/cn/tursom/database/annotations/Param.kt @@ -3,4 +3,5 @@ package cn.tursom.database.annotations @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.VALUE_PARAMETER) -annotation class Param(val value: String) \ No newline at end of file +annotation class Param(val value: String) + diff --git a/database/src/main/kotlin/cn/tursom/database/annotations/Select.kt b/database/src/main/kotlin/cn/tursom/database/annotations/Select.kt new file mode 100644 index 0000000..3756e56 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/annotations/Select.kt @@ -0,0 +1,7 @@ +package cn.tursom.database.annotations + +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class Select(val sql: String) + diff --git a/database/src/main/kotlin/cn/tursom/database/annotations/TableField.kt b/database/src/main/kotlin/cn/tursom/database/annotations/TableField.kt new file mode 100644 index 0000000..5b26a89 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/annotations/TableField.kt @@ -0,0 +1,6 @@ +package cn.tursom.database.annotations + +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class TableField(val fieldName: String) \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/annotations/TableName.kt b/database/src/main/kotlin/cn/tursom/database/annotations/TableName.kt new file mode 100644 index 0000000..4acc644 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/annotations/TableName.kt @@ -0,0 +1,6 @@ +package cn.tursom.database.annotations + +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +annotation class TableName(val value: String) \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/annotations/Update.kt b/database/src/main/kotlin/cn/tursom/database/annotations/Update.kt new file mode 100644 index 0000000..0cf81ec --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/annotations/Update.kt @@ -0,0 +1,7 @@ +package cn.tursom.database.annotations + +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class Update(val sql: String) + diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/AbstractWrapper.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/AbstractWrapper.kt index dc5e19c..40a93d0 100644 --- a/database/src/main/kotlin/cn/tursom/database/wrapper/AbstractWrapper.kt +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/AbstractWrapper.kt @@ -1,8 +1,261 @@ package cn.tursom.database.wrapper -interface AbstractWrapper> : - Wrapper, - Compare, - Nested, - Join, - Func \ No newline at end of file +import java.util.* + + +abstract class AbstractWrapper> : Wrapper, + Compare, + Join, + Func { + @Suppress("UNCHECKED_CAST", "LeakingThis") + private val typedThis = this as Children + + override val table: String get() = TODO() + override val where: Pair> + get() { + if (whereBuilder.endsWith(") OR (")) { + repeat(") OR (".length) { + whereBuilder.deleteCharAt(whereBuilder.length - 1) + } + } + return whereBuilder.toString() to whereParams + } + + protected val whereBuilder = StringBuilder() + protected val whereParams = LinkedList() + + protected fun newWhere() = when { + whereBuilder.endsWith(") OR (") -> whereBuilder + whereBuilder.isNotEmpty() -> whereBuilder.append(" AND ") + else -> whereBuilder.append("(") + } + + override fun allEq(params: Map): Children { + params.forEach { (t, u) -> + t eq u + } + return typedThis + } + + override fun String.eq(value: Any?): Children { + newWhere() + if (value != null) { + whereBuilder.append("$this=?") + whereParams.add(value) + } else { + whereBuilder.append("$this IS NULL") + } + return typedThis + } + + override fun String.ne(value: Any?): Children { + newWhere() + if (value != null) { + whereBuilder.append("$this <> ?") + whereParams.add(value) + } else { + whereBuilder.append("$this IS NOT NULL") + } + return typedThis + } + + override fun String.gt(value: Any): Children { + newWhere() + whereBuilder.append("$this > ?") + whereParams.add(value) + return typedThis + } + + override fun String.ge(value: Any): Children { + newWhere() + whereBuilder.append("$this >= ?") + whereParams.add(value) + return typedThis + } + + override fun String.le(value: Any): Children { + newWhere() + whereBuilder.append("$this < ?") + whereParams.add(value) + return typedThis + } + + override fun String.lt(value: Any): Children { + newWhere() + whereBuilder.append("$this <= ?") + whereParams.add(value) + return typedThis + } + + override fun String.between(val1: Any, val2: Any): Children { + newWhere() + whereBuilder.append("$this BETWEEN ? AND ?") + whereParams.add(val1) + whereParams.add(val2) + return typedThis + } + + override fun String.notBetween(val1: Any, val2: Any): Children { + newWhere() + whereBuilder.append("$this NOT BETWEEN ? AND ?") + whereParams.add(val1) + whereParams.add(val2) + return typedThis + } + + override fun String.like(value: Any): Children { + newWhere() + whereBuilder.append("$this LIKE ?") + whereParams.add(value) + return typedThis + } + + override fun String.notLike(value: Any): Children { + newWhere() + whereBuilder.append("$this NOT LIKE ?") + whereParams.add(value) + return typedThis + } + + override fun String.likeLeft(value: Any): Children { + newWhere() + whereBuilder.append("$this LIKE ?") + whereParams.add("${value}%") + return typedThis + } + + override fun String.likeRight(value: Any): Children { + newWhere() + whereBuilder.append("$this LIKE ?") + whereParams.add("%${value}") + return typedThis + } + + override fun String.isNull(): Children { + newWhere() + whereBuilder.append("$this IS NULL") + return typedThis + } + + override fun String.isNotNull(): Children { + newWhere() + whereBuilder.append("$this IS NOT NULL") + return typedThis + } + + override fun String.`in`(coll: Collection): Children { + newWhere() + if (coll.isEmpty()) return typedThis + whereBuilder.append("$this IN (") + coll.forEach { + whereBuilder.append("?,") + whereParams.add(it) + } + whereBuilder.deleteCharAt(whereBuilder.length - 1) + whereBuilder.append(")") + return typedThis + } + + override fun String.notIn(coll: Collection): Children { + newWhere() + if (coll.isEmpty()) return typedThis + whereBuilder.append("$this NOT IN (") + coll.forEach { + whereBuilder.append("?,") + whereParams.add(it) + } + whereBuilder.deleteCharAt(whereBuilder.length - 1) + whereBuilder.append(")") + return typedThis + } + + override fun String.inSql(inValue: String): Children { + newWhere() + whereBuilder.append("$this IN $inValue") + return typedThis + } + + override fun String.notInSql(inValue: String): Children { + newWhere() + whereBuilder.append("$this NOT IN $inValue") + return typedThis + } + + protected val groupByBuilder = StringBuilder() + override val groupBy: String? + get() = if (groupByBuilder.isEmpty()) null + else groupByBuilder.toString() + + override fun groupBy(columns: Collection): Children { + if (columns.isEmpty()) return typedThis + if (groupByBuilder.isEmpty()) groupByBuilder.append("GROUP BY ") + else groupByBuilder.append(',') + columns.forEach { + groupByBuilder.append(it) + groupByBuilder.append(',') + } + groupByBuilder.deleteCharAt(groupByBuilder.length - 1) + return typedThis + } + + protected val orderByBuilder = StringBuilder() + override val orderBy: String? + get() = if (orderByBuilder.isEmpty()) null + else orderByBuilder.toString() + + override fun orderByAsc(columns: Collection): Children { + if (columns.isEmpty()) return typedThis + if (orderByBuilder.isEmpty()) orderByBuilder.append("ORDER BY ") + else orderByBuilder.append(',') + columns.forEach { + orderByBuilder.append(it) + orderByBuilder.append(" ASC,") + } + orderByBuilder.deleteCharAt(orderByBuilder.length - 1) + return typedThis + } + + override fun orderByDesc(columns: Collection): Children { + if (columns.isEmpty()) return typedThis + if (orderByBuilder.isEmpty()) orderByBuilder.append("ORDER BY ") + else orderByBuilder.append(',') + columns.forEach { + orderByBuilder.append(it) + orderByBuilder.append(" DESC,") + } + orderByBuilder.deleteCharAt(orderByBuilder.length - 1) + return typedThis + } + + override fun orderBy(isAsc: Boolean, columns: Collection): Children { + if (columns.isEmpty()) return typedThis + return if (isAsc) { + orderByAsc(columns) + } else { + orderByDesc(columns) + } + } + + protected val havingBuilder = StringBuilder() + override val having: String? + get() = if (havingBuilder.isEmpty()) null + else havingBuilder.toString() + override val havingParams = LinkedList() + override fun having(sqlHaving: String, vararg params: Any?): Children { + havingBuilder.append(sqlHaving) + havingParams.addAll(params) + return typedThis + } + + override fun or(): Children { + whereBuilder.append(") OR (") + return typedThis + } + + override var last: String = "" + + override fun last(lastSql: String): Children { + last = lastSql + return typedThis + } +} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/Compare.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/Compare.kt index 475c8a5..9f976a2 100644 --- a/database/src/main/kotlin/cn/tursom/database/wrapper/Compare.kt +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/Compare.kt @@ -1,86 +1,53 @@ package cn.tursom.database.wrapper +import cn.tursom.database.SqlUtils import java.io.Serializable -import java.util.function.BiPredicate import kotlin.reflect.KProperty1 -interface Compare : Serializable { - fun allEq(params: Map, V>?): Children { - return this.allEq(params, true) - } +interface Compare : Serializable { + /** + * map 所有属性等于 = + * + * @param params map 类型的参数, key 是字段名, value 是字段值 + * @return children + */ + fun allEq(params: Map): Children + fun Map, Any?>.eq(): Children = allEq(SqlUtils { mapKeys { it.key.tableField } }) - fun allEq(params: Map, V>?, null2IsNull: Boolean): Children { - return this.allEq(true, params, null2IsNull) - } + infix fun String.eq(value: Any?): Children + infix fun KProperty1.eq(value: V): Children = SqlUtils { tableField }.eq(value) - fun allEq(condition: Boolean, params: Map, V>?, null2IsNull: Boolean): Children - fun allEq(filter: BiPredicate, V>?, params: Map, V>?): Children { - return this.allEq(filter, params, true) - } + infix fun String.ne(value: Any?): Children + infix fun KProperty1.ne(value: V): Children = SqlUtils { tableField }.ne(value) - fun allEq(filter: BiPredicate, V>?, params: Map, V>?, null2IsNull: Boolean): Children { - return this.allEq(true, filter, params, null2IsNull) - } + infix fun String.gt(value: Any): Children + infix fun KProperty1.gt(value: V): Children = SqlUtils { tableField }.gt(value) - fun allEq(condition: Boolean, filter: BiPredicate, V>?, params: Map, V>?, null2IsNull: Boolean): Children - fun eq(column: KProperty1, `val`: Any?): Children { - return this.eq(true, column, `val`) - } + infix fun String.ge(value: Any): Children + infix fun KProperty1.ge(value: V): Children = SqlUtils { tableField }.ge(value) - fun eq(condition: Boolean, column: KProperty1, `val`: Any?): Children - fun ne(column: KProperty1, `val`: Any?): Children { - return this.ne(true, column, `val`) - } + infix fun String.le(value: Any): Children + infix fun KProperty1.le(value: V): Children = SqlUtils { tableField }.le(value) - fun ne(condition: Boolean, column: KProperty1, `val`: Any?): Children - fun gt(column: KProperty1, `val`: Any?): Children { - return this.gt(true, column, `val`) - } + infix fun String.lt(value: Any): Children + infix fun KProperty1.lt(value: V): Children = SqlUtils { tableField }.lt(value) - fun gt(condition: Boolean, column: KProperty1, `val`: Any?): Children - fun ge(column: KProperty1, `val`: Any?): Children { - return this.ge(true, column, `val`) - } + fun String.between(val1: Any, val2: Any): Children + fun KProperty1.between(val1: V, val2: V): Children = SqlUtils { tableField }.between(val1, val2) - fun ge(condition: Boolean, column: KProperty1, `val`: Any?): Children - fun lt(column: KProperty1, `val`: Any?): Children { - return this.lt(true, column, `val`) - } + fun String.notBetween(val1: Any, val2: Any): Children + fun KProperty1.notBetween(val1: V, val2: V): Children = SqlUtils { tableField }.notBetween(val1, val2) - fun lt(condition: Boolean, column: KProperty1, `val`: Any?): Children - fun le(column: KProperty1, `val`: Any?): Children { - return this.le(true, column, `val`) - } + infix fun String.like(value: Any): Children + infix fun KProperty1.like(value: V): Children = SqlUtils { tableField }.like(value) - fun le(condition: Boolean, column: KProperty1, `val`: Any?): Children - fun between(column: KProperty1, val1: Any?, val2: Any?): Children { - return this.between(true, column, val1, val2) - } + infix fun String.notLike(value: Any): Children + infix fun KProperty1.notLike(value: V): Children = SqlUtils { tableField }.notLike(value) - fun between(condition: Boolean, column: KProperty1, val1: Any?, val2: Any?): Children - fun notBetween(column: KProperty1, val1: Any?, val2: Any?): Children { - return this.notBetween(true, column, val1, val2) - } + infix fun String.likeLeft(value: Any): Children + infix fun KProperty1.likeLeft(value: V): Children = SqlUtils { tableField }.likeLeft(value) - fun notBetween(condition: Boolean, column: KProperty1, val1: Any?, val2: Any?): Children - fun like(column: KProperty1, `val`: Any?): Children { - return this.like(true, column, `val`) - } + infix fun String.likeRight(value: Any): Children + infix fun KProperty1.likeRight(value: V): Children = SqlUtils { tableField }.likeRight(value) - fun like(condition: Boolean, column: KProperty1, `val`: Any?): Children - fun notLike(column: KProperty1, `val`: Any?): Children { - return this.notLike(true, column, `val`) - } - - fun notLike(condition: Boolean, column: KProperty1, `val`: Any?): Children - fun likeLeft(column: KProperty1, `val`: Any?): Children { - return this.likeLeft(true, column, `val`) - } - - fun likeLeft(condition: Boolean, column: KProperty1, `val`: Any?): Children - fun likeRight(column: KProperty1, `val`: Any?): Children { - return this.likeRight(true, column, `val`) - } - - fun likeRight(condition: Boolean, column: KProperty1, `val`: Any?): Children } \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/Func.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/Func.kt index e16c8a5..e06d982 100644 --- a/database/src/main/kotlin/cn/tursom/database/wrapper/Func.kt +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/Func.kt @@ -1,92 +1,171 @@ package cn.tursom.database.wrapper +import cn.tursom.database.SqlUtils import java.io.Serializable import kotlin.reflect.KProperty1 -interface Func : Serializable { - fun isNull(column: KProperty1): Children { - return this.isNull(true, column) - } +interface Func : Serializable { + /** + * 字段 IS NULL + *

例: isNull("name")

+ * + * @this 字段 + * @return children + */ + fun KProperty1.isNull(): Children = SqlUtils { tableField }.isNull() + fun String.isNull(): Children - fun isNull(condition: Boolean, column: KProperty1): Children - fun isNotNull(column: KProperty1): Children { - return this.isNotNull(true, column) - } + /** + * 字段 IS NOT NULL + *

例: isNotNull("name")

+ * + * @this 字段 + * @return children + */ + fun KProperty1.isNotNull(): Children = SqlUtils { tableField }.isNotNull() + fun String.isNotNull(): Children - fun isNotNull(condition: Boolean, column: KProperty1): Children - fun `in`(column: KProperty1, coll: Collection<*>?): Children { - return this.`in`(true, column, coll) - } + /** + * 字段 IN (value.get(0), value.get(1), ...) + *

例: in("id", Arrays.asList(1, 2, 3, 4, 5))

+ * + *
  • 如果集合为 empty 则不会进行 sql 拼接
  • + * + * @this 字段 + * @param coll 数据集合 + * @return children + */ + infix fun KProperty1.`in`(coll: Collection): Children = SqlUtils { tableField }.`in`(coll as Collection) + fun String.`in`(coll: Collection): Children - fun `in`(condition: Boolean, column: KProperty1, coll: Collection<*>?): Children - fun `in`(column: KProperty1, vararg values: Any?): Children { - return this.`in`(true, column, *values) - } + /** + * 字段 IN (v0, v1, ...) + *

    例: in("id", 1, 2, 3, 4, 5)

    + * + *
  • 如果动态数组为 empty 则不会进行 sql 拼接
  • + * + * @this 字段 + * @param values 数据数组 + * @return children + */ + fun KProperty1.`in`(vararg values: R): Children = SqlUtils { tableField }.`in`(values.asList()) + fun String.`in`(vararg values: Any?): Children = `in`(values.asList()) - fun `in`(condition: Boolean, column: KProperty1, vararg values: Any?): Children { - return this.`in`(condition, column, values.asList()) - } - fun notIn(column: KProperty1, coll: Collection<*>?): Children { - return this.notIn(true, column, coll) - } + /** + * 字段 NOT IN (value.get(0), value.get(1), ...) + *

    例: notIn("id", Arrays.asList(1, 2, 3, 4, 5))

    + * + * @this 字段 + * @param coll 数据集合 + * @return children + */ + infix fun KProperty1.notIn(coll: Collection): Children = SqlUtils { tableField }.notIn(coll) + fun String.notIn(coll: Collection): Children - fun notIn(condition: Boolean, column: KProperty1, coll: Collection<*>?): Children - fun notIn(column: KProperty1, vararg value: Any?): Children { - return this.notIn(true, column, *value) - } + /** + * 字段 NOT IN (v0, v1, ...) + *

    例: notIn("id", 1, 2, 3, 4, 5)

    + * + * @this 字段 + * @param values 数据数组 + * @return children + */ + fun KProperty1.notIn(vararg values: R): Children = notIn(values.asList()) + fun String.notIn(vararg value: Any?): Children = notIn(value.asList()) - fun notIn(condition: Boolean, column: KProperty1, vararg values: Any?): Children { - return this.notIn(condition, column, values.asList()) - } + /** + * 字段 IN ( sql语句 ) + *

    !! sql 注入方式的 in 方法 !!

    + *

    例1: inSql("id", "1, 2, 3, 4, 5, 6")

    + *

    例2: inSql("id", "select id from table where id < 3")

    + * + * @this 字段 + * @param inValue sql语句 + * @return children + */ + infix fun KProperty1.inSql(inValue: String): Children = SqlUtils { tableField }.inSql(inValue) + fun String.inSql(inValue: String): Children - fun inSql(column: KProperty1, inValue: String?): Children { - return this.inSql(true, column, inValue) - } + /** + * 字段 NOT IN ( sql语句 ) + *

    !! sql 注入方式的 not in 方法 !!

    + *

    例1: notInSql("id", "1, 2, 3, 4, 5, 6")

    + *

    例2: notInSql("id", "select id from table where id < 3")

    + * + * @this 字段 + * @param inValue sql语句 ---> 1,2,3,4,5,6 或者 select id from table where id < 3 + * @return children + */ + infix fun KProperty1.notInSql(inValue: String): Children = SqlUtils { tableField }.notInSql(inValue) + fun String.notInSql(inValue: String): Children - fun inSql(condition: Boolean, column: KProperty1, inValue: String?): Children - fun notInSql(column: KProperty1, inValue: String?): Children { - return this.notInSql(true, column, inValue) - } + val groupBy: String? - fun notInSql(condition: Boolean, column: KProperty1, inValue: String?): Children - fun groupBy(column: KProperty1): Children { - return this.groupBy(true, column) - } + /** + * 分组:GROUP BY 字段, ... + *

    例: groupBy("id", "name")

    + * + * @param columns 字段数组 + * @return children + */ + fun groupBy(columns: Iterable>): Children = groupBy(SqlUtils { columns.map { it.tableField } }) + fun groupBy(vararg columns: KProperty1): Children = groupBy(columns.asList()) + fun groupBy(columns: Collection): Children + fun groupBy(vararg columns: String): Children = groupBy(columns.asList()) - fun groupBy(vararg columns: KProperty1): Children { - return this.groupBy(true, *columns) - } + val orderBy: String? - fun groupBy(condition: Boolean, vararg columns: KProperty1): Children - fun orderByAsc(column: KProperty1): Children { - return this.orderByAsc(true, column) - } + /** + * 排序:ORDER BY 字段, ... ASC + *

    例: orderByAsc("id", "name")

    + * + * @param columns 字段数组 + * @return children + */ + fun orderByAsc(columns: Iterable>): Children = orderByAsc(SqlUtils { columns.map { it.tableField } }) + fun orderByAsc(vararg columns: KProperty1): Children = orderByAsc(columns.asList()) + fun orderByAsc(columns: Collection): Children + fun orderByAsc(vararg columns: String): Children = orderByAsc(columns.asList()) - fun orderByAsc(vararg columns: KProperty1): Children { - return this.orderByAsc(true, *columns) - } + /** + * 排序:ORDER BY 字段, ... DESC + *

    例: orderByDesc("id", "name")

    + * + * @param columns 字段数组 + * @return children + */ + fun orderByDesc(columns: Iterable>): Children = orderByDesc(SqlUtils { columns.map { it.tableField } }) + fun orderByDesc(vararg columns: KProperty1): Children = orderByDesc(columns.asList()) + fun orderByDesc(columns: Collection): Children + fun orderByDesc(vararg columns: String): Children = orderByDesc(columns.asList()) - fun orderByAsc(condition: Boolean, vararg columns: KProperty1): Children { - return orderBy(condition, true, *columns) - } + /** + * 排序:ORDER BY 字段, ... + *

    例: orderBy("id", "name")

    + * + * @param isAsc 是否是 ASC 排序 + * @param columns 字段数组 + * @return children + */ + fun orderBy(isAsc: Boolean, columns: Iterable>): Children = orderBy(isAsc, SqlUtils { columns.map { it.tableField } }) + fun orderBy(isAsc: Boolean, vararg columns: KProperty1): Children = orderBy(isAsc, columns.asList()) + fun orderBy(isAsc: Boolean, columns: Collection): Children + fun orderBy(isAsc: Boolean, vararg columns: String): Children = orderBy(isAsc, columns.asList()) - fun orderByDesc(column: KProperty1): Children { - return this.orderByDesc(true, column) - } + val having: String? + val havingParams: Collection - fun orderByDesc(vararg columns: KProperty1): Children { - return this.orderByDesc(true, *columns) - } + /** + * HAVING ( sql语句 ) + *

    例1: having("sum(age) > 10")

    + *

    例2: having("sum(age) > ?", 10)

    + * + * @param sqlHaving sql 语句 + * @param params 参数数组 + * @return children + */ + fun having(sqlHaving: String, vararg params: Any?): Children - fun orderByDesc(condition: Boolean, vararg columns: KProperty1): Children { - return orderBy(condition, false, *columns) - } - fun orderBy(condition: Boolean, isAsc: Boolean, vararg columns: KProperty1): 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 } \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/IUpdateWrapper.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/IUpdateWrapper.kt new file mode 100644 index 0000000..94a1748 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/IUpdateWrapper.kt @@ -0,0 +1,5 @@ +package cn.tursom.database.wrapper + +interface IUpdateWrapper : Wrapper { + val sqlSet: Pair> +} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/Join.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/Join.kt index e325613..ee19ebf 100644 --- a/database/src/main/kotlin/cn/tursom/database/wrapper/Join.kt +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/Join.kt @@ -2,32 +2,12 @@ package cn.tursom.database.wrapper import java.io.Serializable -interface Join : 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 +interface Join : Serializable { + fun or(): Children + //fun apply(applySql: String, vararg value: Any?): Children + var last: String + fun last(lastSql: String): Children + //fun comment(comment: String): Children + //fun exists(existsSql: String): Children + //fun notExists(notExistsSql: String): Children } \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/Nested.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/Nested.kt deleted file mode 100644 index d42e66c..0000000 --- a/database/src/main/kotlin/cn/tursom/database/wrapper/Nested.kt +++ /dev/null @@ -1,22 +0,0 @@ -package cn.tursom.database.wrapper - -import java.io.Serializable -import java.util.function.Consumer - -interface Nested : Serializable { - fun and(consumer: Consumer?): Children { - return this.and(true, consumer) - } - - fun and(condition: Boolean, consumer: Consumer?): Children - fun or(consumer: Consumer?): Children { - return this.or(true, consumer) - } - - fun or(condition: Boolean, consumer: Consumer?): Children - fun nested(consumer: Consumer?): Children { - return this.nested(true, consumer) - } - - fun nested(condition: Boolean, consumer: Consumer?): Children -} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/Query.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/Query.kt new file mode 100644 index 0000000..8cfaf42 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/Query.kt @@ -0,0 +1,27 @@ +package cn.tursom.database.wrapper + +import cn.tursom.database.SqlUtils +import java.io.Serializable +import kotlin.reflect.KProperty + +/** + * @author miemie + * @since 2018-12-12 + */ +interface Query : Serializable { + /** + * 查询条件 SQL 片段 + */ + val sqlSelect: String + + /** + * 设置查询字段 + * + * @param columns 字段数组 + * @return children + */ + fun select(columns: Collection): Children + fun select(vararg columns: String): Children = select(columns.asList()) + fun select(columns: Iterable>): Children = select(columns.map { SqlUtils { it.selectField } }) + fun select(vararg columns: KProperty): Children = select(columns.asList()) +} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/QueryWrapper.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/QueryWrapper.kt new file mode 100644 index 0000000..6310e26 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/QueryWrapper.kt @@ -0,0 +1,14 @@ +package cn.tursom.database.wrapper + +class QueryWrapper : AbstractWrapper>(), Query> { + private val selectBuilder = StringBuilder() + override val sqlSelect: String get() = selectBuilder.dropLast(1).toString() + + override fun select(columns: Collection): QueryWrapper { + columns.forEach { + selectBuilder.append(it) + selectBuilder.append(',') + } + return this + } +} \ No newline at end of file diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/Update.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/Update.kt new file mode 100644 index 0000000..eeb4652 --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/Update.kt @@ -0,0 +1,14 @@ +package cn.tursom.database.wrapper + +import cn.tursom.database.SqlUtils +import java.io.Serializable +import kotlin.reflect.KProperty1 + +interface Update : Serializable { + fun setSql(sql: String, values: Collection): Children + fun setSql(sql: String, vararg values: Any?): Children = setSql(sql, values.asList()) + + infix fun String.set(value: T): Children + infix fun KProperty1.set(value: R): Children = set(SqlUtils { tableField }) +} + diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/UpdateWrapper.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/UpdateWrapper.kt new file mode 100644 index 0000000..12a690b --- /dev/null +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/UpdateWrapper.kt @@ -0,0 +1,25 @@ +package cn.tursom.database.wrapper + +import java.util.* + +class UpdateWrapper : AbstractWrapper>(), IUpdateWrapper, Update> { + private val setBuilder = StringBuilder() + private val params = LinkedList() + + override val sqlSet: Pair> + get() = setBuilder.toString() to params + + override fun String.set(value: T): UpdateWrapper { + if (setBuilder.isNotEmpty()) setBuilder.append(",") + setBuilder.append("$this=?") + params.add(value) + return this@UpdateWrapper + } + + override fun setSql(sql: String, values: Collection): UpdateWrapper { + setBuilder.append(sql) + params.addAll(values) + return this + } +} + diff --git a/database/src/main/kotlin/cn/tursom/database/wrapper/Wrapper.kt b/database/src/main/kotlin/cn/tursom/database/wrapper/Wrapper.kt index a573cdb..e6bb4fd 100644 --- a/database/src/main/kotlin/cn/tursom/database/wrapper/Wrapper.kt +++ b/database/src/main/kotlin/cn/tursom/database/wrapper/Wrapper.kt @@ -1,4 +1,10 @@ package cn.tursom.database.wrapper -interface Wrapper +interface Wrapper { + val table: String + val where: Pair> + + data class Where(val sql: String, val params: Collection? = null) +} + diff --git a/settings.gradle b/settings.gradle index 30a5c1b..ee96b70 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,6 +9,7 @@ include 'web:web-coroutine' include 'microservices' include 'database:database-mysql' include 'database:mongodb' +include 'database:mongodb:mongodb-async' include 'utils:ws-client' include 'utils:mail' include 'utils:csv' \ No newline at end of file diff --git a/src/main/kotlin/cn/tursom/core/Disposable.kt b/src/main/kotlin/cn/tursom/core/Disposable.kt new file mode 100644 index 0000000..7c57904 --- /dev/null +++ b/src/main/kotlin/cn/tursom/core/Disposable.kt @@ -0,0 +1,19 @@ +package cn.tursom.core + +import java.util.concurrent.atomic.AtomicReference + +class Disposable { + private var value = AtomicReference() + fun set(value: T) { + this.value.set(value) + } + + fun get(): T? { + val value = value.get() ?: return null + return if (this.value.compareAndSet(value, null)) { + value + } else { + null + } + } +} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/AsyncIterator.kt b/utils/src/main/kotlin/cn/tursom/utils/AsyncIterator.kt new file mode 100644 index 0000000..99bc7c6 --- /dev/null +++ b/utils/src/main/kotlin/cn/tursom/utils/AsyncIterator.kt @@ -0,0 +1,20 @@ +package cn.tursom.utils + +interface AsyncIterator { + /** + * Returns the next element in the iteration. + */ + suspend operator fun next(): T + + /** + * Returns `true` if the iteration has more elements. + */ + suspend operator fun hasNext(): Boolean +} + +suspend inline fun AsyncIterator.forEach(action: (T) -> Unit) { + while (hasNext()) { + val element = next() + action(element) + } +} \ No newline at end of file diff --git a/utils/src/main/kotlin/cn/tursom/utils/Tools.kt b/utils/src/main/kotlin/cn/tursom/utils/Tools.kt index a51a970..b5d967f 100644 --- a/utils/src/main/kotlin/cn/tursom/utils/Tools.kt +++ b/utils/src/main/kotlin/cn/tursom/utils/Tools.kt @@ -3,13 +3,17 @@ package cn.tursom.utils import com.google.gson.Gson import com.google.gson.GsonBuilder import kotlinx.coroutines.* +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method +import java.lang.reflect.Proxy import java.util.concurrent.Executor import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine +import kotlin.reflect.jvm.javaMethod @Suppress("unused", "SpellCheckingInspection") -val gson =GsonBuilder() +val gson = GsonBuilder() .registerTypeAdapterFactory(GsonDataTypeAdaptor.FACTORY) .create()