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 {
return RespBean(HttpStatus.ERROR, message, data)
return RespBean(code, message, data)
}

View File

@ -26,7 +26,7 @@ class JwtAuthenticationHandler(
tokenService.verifyToken(user)
event.setUser(user)
event.next()
} catch (e: Exception) {
} catch (e: Throwable) {
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()}" }
val resObj = when(e) {
is Meta -> {

View File

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

View File

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

View File

@ -23,7 +23,7 @@ class DefaultResponseHandler: ResponseHandlerInterface {
.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()}" }
val failure = ctx.failure()
if (failure == null) {

View File

@ -4,5 +4,5 @@ import io.vertx.ext.web.RoutingContext
interface ResponseHandlerInterface {
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)
}
responseHandler.normal(ctx, resObj, routeInfo.customizeResp)
} catch (e: Exception) {
} catch (e: Throwable) {
responseHandler.exception(ctx, e)
}
}

View File

@ -1,45 +1,27 @@
package org.aikrai.vertx.db
import cn.hutool.core.util.StrUtil
import io.vertx.kotlin.coroutines.coAwait
import io.vertx.sqlclient.Row
import io.vertx.sqlclient.SqlClient
import io.vertx.sqlclient.templates.SqlTemplate
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.utlis.Meta
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.reflect.KProperty1
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> {
var sqlClient: SqlClient? = null
private val logger = KotlinLogging.logger { }
private val conditions = CopyOnWriteArrayList<QueryCondition>()
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> {
conditions.add(
QueryCondition(

View File

@ -28,35 +28,65 @@ import kotlin.reflect.KProperty1
open class RepositoryImpl<TId, TEntity : Any>(
private val sqlClient: SqlClient
) : 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 clazz: Class<TEntity> = (this::class.java.genericSuperclass as ParameterizedType)
.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 {
!Modifier.isStatic(it.modifiers) &&
!it.isSynthetic &&
!it.isAnnotationPresent(Transient::class.java)
}.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 fields: List<Field> by lazy {
fieldsCache.getOrPut(clazz.simpleName) {
clazz.declaredFields.filter {
!Modifier.isStatic(it.modifiers) &&
!it.isSynthetic &&
!it.isAnnotationPresent(Transient::class.java)
}.onEach { it.isAccessible = true }
}
}
private val idField: Field =
clazz.declaredFields.find { it.isAnnotationPresent(TableId::class.java) }?.also { it.isAccessible = true }
?: throw IllegalArgumentException("No @Id field found in ${clazz.simpleName}")
private val fieldMappings: Map<String, String> by lazy {
fieldMappingCache.getOrPut(clazz.simpleName) {
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 idFieldName: String = idField.getAnnotation(TableField::class.java)?.value?.takeIf { it.isNotBlank() }
?: StrUtil.toUnderlineCase(idField.name)
private val idField: Field by lazy {
idFieldCache.getOrPut(clazz.simpleName) {
clazz.declaredFields.find { it.isAnnotationPresent(TableId::class.java) }
?.also { it.isAccessible = true }
?: throw IllegalArgumentException("No @Id field in ${clazz.simpleName}")
}
}
private val tableName: String = clazz.getAnnotation(TableName::class.java)?.value?.takeIf { it.isNotBlank() }
?: StrUtil.toUnderlineCase(clazz.simpleName)
private val idFieldName: String by lazy {
idFieldNameCache.getOrPut(clazz.simpleName) {
idField.getAnnotation(TableField::class.java)?.value?.takeIf { it.isNotBlank() }
?: StrUtil.toUnderlineCase(idField.name)
}
}
private val tableName: String by lazy {
tableNameCache.getOrPut(clazz.simpleName) {
clazz.getAnnotation(TableName::class.java)?.value?.takeIf { it.isNotBlank() }
?: StrUtil.toUnderlineCase(clazz.simpleName)
}
}
override suspend fun create(t: TEntity): Int {
try {
@ -287,8 +317,8 @@ open class RepositoryImpl<TId, TEntity : Any>(
suspend fun queryBuilder(qClazz: Class<Any>? = null): QueryWrapper<TEntity> {
val qClass = qClazz ?: clazz
val connection = getConnection()
val queryWrapper = querySqlMap.getOrPut(qClass.simpleName) {
QueryWrapperImpl(qClass)
val queryWrapper = queryClientCache.getOrPut(qClass.simpleName) {
QueryWrapperImpl(qClass, tableName, fieldMappings)
} as QueryWrapperImpl<TEntity>
queryWrapper.sqlClient = connection
return queryWrapper
@ -308,7 +338,7 @@ open class RepositoryImpl<TId, TEntity : Any>(
// 通用获取或创建 SQL 模板的方法
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)
}