refactor(core): 移除单例注解和优化异常处理

refactor(core): 优化QueryWrapperImpl类结构和缓存逻辑
This commit is contained in:
AiKrai 2025-02-12 10:25:42 +08:00
parent c3cb7fae37
commit 098fa02ca6
10 changed files with 66 additions and 56 deletions

View File

@ -37,7 +37,7 @@ data class RespBean(
} }
fun failure(code: Int, message: String, data: Any? = null): RespBean { fun failure(code: Int, message: String, data: Any? = null): RespBean {
return RespBean(HttpStatus.ERROR, message, data) return RespBean(code, message, data)
} }

View File

@ -26,7 +26,7 @@ class JwtAuthenticationHandler(
tokenService.verifyToken(user) tokenService.verifyToken(user)
event.setUser(user) event.setUser(user)
event.next() event.next()
} catch (e: Exception) { } catch (e: Throwable) {
event.fail(401, Meta.unauthorized(e.message ?: "token")) event.fail(401, Meta.unauthorized(e.message ?: "token"))
} }
} }

View File

@ -48,7 +48,7 @@ class ResponseHandler: ResponseHandlerInterface {
} }
// 业务异常处理 // 业务异常处理
override suspend fun exception(ctx: RoutingContext, e: Exception) { override suspend fun exception(ctx: RoutingContext, e: Throwable) {
logger.error { "${ctx.request().uri()}: ${e.stackTraceToString()}" } logger.error { "${ctx.request().uri()}: ${e.stackTraceToString()}" }
val resObj = when(e) { val resObj = when(e) {
is Meta -> { is Meta -> {

View File

@ -3,11 +3,9 @@ package app.domain.account
import app.base.domain.auth.modle.AccountRoleDTO import app.base.domain.auth.modle.AccountRoleDTO
import app.domain.account.modle.AccountRoleAccessDTO import app.domain.account.modle.AccountRoleAccessDTO
import com.google.inject.Inject import com.google.inject.Inject
import com.google.inject.Singleton
import io.vertx.sqlclient.SqlClient import io.vertx.sqlclient.SqlClient
import org.aikrai.vertx.db.RepositoryImpl import org.aikrai.vertx.db.RepositoryImpl
@Singleton
class AccountRepositoryImpl @Inject constructor( class AccountRepositoryImpl @Inject constructor(
sqlClient: SqlClient sqlClient: SqlClient
) : RepositoryImpl<Long, Account>(sqlClient), AccountRepository { ) : RepositoryImpl<Long, Account>(sqlClient), AccountRepository {

View File

@ -64,9 +64,9 @@ class WebVerticle @Inject constructor(
rootRouter.route("/api" + "*").subRouter(router) rootRouter.route("/api" + "*").subRouter(router)
router.route() router.route()
.handler(corsHandler) .handler(corsHandler)
.failureHandler(errorHandler)
.handler(BodyHandler.create()) .handler(BodyHandler.create())
.handler(logHandler) .handler(logHandler)
.failureHandler(errorHandler)
val authHandler = JwtAuthenticationHandler(coroutineScope, tokenService, context, snowflake) val authHandler = JwtAuthenticationHandler(coroutineScope, tokenService, context, snowflake)
router.route("/*").handler(authHandler) router.route("/*").handler(authHandler)
@ -92,13 +92,13 @@ class WebVerticle @Inject constructor(
if (failure != null) { if (failure != null) {
logger.error { "${ctx.request().uri()}: ${failure.stackTraceToString()}" } logger.error { "${ctx.request().uri()}: ${failure.stackTraceToString()}" }
val resObj = when (failure) { val resObj = when (failure) {
is Meta -> RespBean.failure("${failure.name}:${failure.message}", failure.data) is Meta -> RespBean.failure(ctx.statusCode(),"${failure.name}:${failure.message}", failure.data)
else -> RespBean.failure("${failure.javaClass.simpleName}${if (failure.message != null) ":${failure.message}" else ""}") else -> RespBean.failure("${failure.javaClass.simpleName}${if (failure.message != null) ":${failure.message}" else ""}")
} }
val resStr = JsonUtil.toJsonStr(resObj) val resStr = JsonUtil.toJsonStr(resObj)
ctx.put("responseData", resStr) ctx.put("responseData", resStr)
ctx.response() ctx.response()
.setStatusCode(if (ctx.statusCode() != 200) ctx.statusCode() else 500) .setStatusCode(ctx.statusCode())
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=utf-8") .putHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=utf-8")
.end(resStr) .end(resStr)
} else { } else {

View File

@ -23,7 +23,7 @@ class DefaultResponseHandler: ResponseHandlerInterface {
.end(resStr) .end(resStr)
} }
override suspend fun exception(ctx: RoutingContext, e: Exception) { override suspend fun exception(ctx: RoutingContext, e: Throwable) {
logger.error { "${ctx.request().uri()}: ${ctx.failure().stackTraceToString()}" } logger.error { "${ctx.request().uri()}: ${ctx.failure().stackTraceToString()}" }
val failure = ctx.failure() val failure = ctx.failure()
if (failure == null) { if (failure == null) {

View File

@ -4,5 +4,5 @@ import io.vertx.ext.web.RoutingContext
interface ResponseHandlerInterface { interface ResponseHandlerInterface {
suspend fun normal(ctx: RoutingContext, responseData: Any?, customizeResponse: Boolean = false) suspend fun normal(ctx: RoutingContext, responseData: Any?, customizeResponse: Boolean = false)
suspend fun exception(ctx: RoutingContext, e: Exception) suspend fun exception(ctx: RoutingContext, e: Throwable)
} }

View File

@ -110,7 +110,7 @@ class RouterBuilder(
routeInfo.kFunction.call(instance, *params) routeInfo.kFunction.call(instance, *params)
} }
responseHandler.normal(ctx, resObj, routeInfo.customizeResp) responseHandler.normal(ctx, resObj, routeInfo.customizeResp)
} catch (e: Exception) { } catch (e: Throwable) {
responseHandler.exception(ctx, e) responseHandler.exception(ctx, e)
} }
} }

View File

@ -1,45 +1,27 @@
package org.aikrai.vertx.db package org.aikrai.vertx.db
import cn.hutool.core.util.StrUtil
import io.vertx.kotlin.coroutines.coAwait import io.vertx.kotlin.coroutines.coAwait
import io.vertx.sqlclient.Row import io.vertx.sqlclient.Row
import io.vertx.sqlclient.SqlClient import io.vertx.sqlclient.SqlClient
import io.vertx.sqlclient.templates.SqlTemplate import io.vertx.sqlclient.templates.SqlTemplate
import mu.KotlinLogging import mu.KotlinLogging
import org.aikrai.vertx.db.annotation.TableField
import org.aikrai.vertx.db.annotation.TableName
import org.aikrai.vertx.jackson.JsonUtil import org.aikrai.vertx.jackson.JsonUtil
import org.aikrai.vertx.utlis.Meta import org.aikrai.vertx.utlis.Meta
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import kotlin.reflect.KProperty1 import kotlin.reflect.KProperty1
class QueryWrapperImpl<T : Any>( class QueryWrapperImpl<T : Any>(
private val clazz: Class<T> private val clazz: Class<T>,
private val tableName: String,
private val fieldMappings: Map<String, String>,
) : QueryWrapper<T> { ) : QueryWrapper<T> {
var sqlClient: SqlClient? = null var sqlClient: SqlClient? = null
private val logger = KotlinLogging.logger { } private val logger = KotlinLogging.logger { }
private val conditions = CopyOnWriteArrayList<QueryCondition>() private val conditions = CopyOnWriteArrayList<QueryCondition>()
private val sqlMap = ConcurrentHashMap<String, String>() private val sqlMap = ConcurrentHashMap<String, String>()
private val fields: List<Field> = clazz.declaredFields.filter {
!it.isAnnotationPresent(Transient::class.java) &&
!Modifier.isStatic(it.modifiers) &&
!it.isSynthetic
}.onEach { it.isAccessible = true }
private val fieldMappings: Map<String, String> = fields.associate { field ->
val fieldAnnotation = field.getAnnotation(TableField::class.java)
val fieldName = fieldAnnotation?.value?.takeIf { it.isNotBlank() }
?: StrUtil.toUnderlineCase(field.name)
field.name to fieldName
}
private val tableName: String = clazz.getAnnotation(TableName::class.java)?.value?.takeIf { it.isNotBlank() }
?: StrUtil.toUnderlineCase(clazz.simpleName)
override fun select(vararg columns: String): QueryWrapper<T> { override fun select(vararg columns: String): QueryWrapper<T> {
conditions.add( conditions.add(
QueryCondition( QueryCondition(

View File

@ -28,35 +28,65 @@ import kotlin.reflect.KProperty1
open class RepositoryImpl<TId, TEntity : Any>( open class RepositoryImpl<TId, TEntity : Any>(
private val sqlClient: SqlClient private val sqlClient: SqlClient
) : Repository<TId, TEntity> { ) : Repository<TId, TEntity> {
companion object {
// classInfoCache
private val fieldsCache = ConcurrentHashMap<String, List<Field>>()
private val fieldMappingCache = ConcurrentHashMap<String, Map<String, String>>()
private val idFieldCache = ConcurrentHashMap<String, Field>()
private val idFieldNameCache = ConcurrentHashMap<String, String>()
private val tableNameCache = ConcurrentHashMap<String, String>()
// sqlCache
private val baseSqlCache = ConcurrentHashMap<String, ConcurrentHashMap<String, String>>()
private val queryClientCache = ConcurrentHashMap<String, Any>()
}
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
private val clazz: Class<TEntity> = (this::class.java.genericSuperclass as ParameterizedType) private val clazz: Class<TEntity> = (this::class.java.genericSuperclass as ParameterizedType)
.actualTypeArguments[1] as Class<TEntity> .actualTypeArguments[1] as Class<TEntity>
private val sqlMap = ConcurrentHashMap<String, ConcurrentHashMap<String, String>>()
private val querySqlMap = ConcurrentHashMap<String, Any>()
// 缓存字段和映射 // 缓存字段和映射
private val fields: List<Field> = clazz.declaredFields.filter { private val fields: List<Field> by lazy {
fieldsCache.getOrPut(clazz.simpleName) {
clazz.declaredFields.filter {
!Modifier.isStatic(it.modifiers) && !Modifier.isStatic(it.modifiers) &&
!it.isSynthetic && !it.isSynthetic &&
!it.isAnnotationPresent(Transient::class.java) !it.isAnnotationPresent(Transient::class.java)
}.onEach { it.isAccessible = true } }.onEach { it.isAccessible = true }
}
}
private val fieldMappings: Map<String, String> = fields.associate { field -> private val fieldMappings: Map<String, String> by lazy {
fieldMappingCache.getOrPut(clazz.simpleName) {
fields.associate { field ->
val fieldAnnotation = field.getAnnotation(TableField::class.java) val fieldAnnotation = field.getAnnotation(TableField::class.java)
val fieldName = fieldAnnotation?.value?.takeIf { it.isNotBlank() } val fieldName = fieldAnnotation?.value?.takeIf { it.isNotBlank() }
?: StrUtil.toUnderlineCase(field.name) ?: StrUtil.toUnderlineCase(field.name)
field.name to fieldName field.name to fieldName
} }
}
}
private val idField: Field = private val idField: Field by lazy {
clazz.declaredFields.find { it.isAnnotationPresent(TableId::class.java) }?.also { it.isAccessible = true } idFieldCache.getOrPut(clazz.simpleName) {
?: throw IllegalArgumentException("No @Id field found in ${clazz.simpleName}") clazz.declaredFields.find { it.isAnnotationPresent(TableId::class.java) }
?.also { it.isAccessible = true }
?: throw IllegalArgumentException("No @Id field in ${clazz.simpleName}")
}
}
private val idFieldName: String = idField.getAnnotation(TableField::class.java)?.value?.takeIf { it.isNotBlank() } private val idFieldName: String by lazy {
idFieldNameCache.getOrPut(clazz.simpleName) {
idField.getAnnotation(TableField::class.java)?.value?.takeIf { it.isNotBlank() }
?: StrUtil.toUnderlineCase(idField.name) ?: StrUtil.toUnderlineCase(idField.name)
}
}
private val tableName: String = clazz.getAnnotation(TableName::class.java)?.value?.takeIf { it.isNotBlank() } private val tableName: String by lazy {
tableNameCache.getOrPut(clazz.simpleName) {
clazz.getAnnotation(TableName::class.java)?.value?.takeIf { it.isNotBlank() }
?: StrUtil.toUnderlineCase(clazz.simpleName) ?: StrUtil.toUnderlineCase(clazz.simpleName)
}
}
override suspend fun create(t: TEntity): Int { override suspend fun create(t: TEntity): Int {
try { try {
@ -287,8 +317,8 @@ open class RepositoryImpl<TId, TEntity : Any>(
suspend fun queryBuilder(qClazz: Class<Any>? = null): QueryWrapper<TEntity> { suspend fun queryBuilder(qClazz: Class<Any>? = null): QueryWrapper<TEntity> {
val qClass = qClazz ?: clazz val qClass = qClazz ?: clazz
val connection = getConnection() val connection = getConnection()
val queryWrapper = querySqlMap.getOrPut(qClass.simpleName) { val queryWrapper = queryClientCache.getOrPut(qClass.simpleName) {
QueryWrapperImpl(qClass) QueryWrapperImpl(qClass, tableName, fieldMappings)
} as QueryWrapperImpl<TEntity> } as QueryWrapperImpl<TEntity>
queryWrapper.sqlClient = connection queryWrapper.sqlClient = connection
return queryWrapper return queryWrapper
@ -308,7 +338,7 @@ open class RepositoryImpl<TId, TEntity : Any>(
// 通用获取或创建 SQL 模板的方法 // 通用获取或创建 SQL 模板的方法
private fun getOrCreateSql(tableName: String, key: String, sqlProvider: () -> String): String { private fun getOrCreateSql(tableName: String, key: String, sqlProvider: () -> String): String {
val tableSqlMap = sqlMap.computeIfAbsent(tableName) { ConcurrentHashMap() } val tableSqlMap = baseSqlCache.getOrPut(tableName) { ConcurrentHashMap() }
return tableSqlMap.getOrPut(key, sqlProvider) return tableSqlMap.getOrPut(key, sqlProvider)
} }