diff --git a/src/main/java/neatlogic/framework/common/config/Config.java b/src/main/java/neatlogic/framework/common/config/Config.java
index 72d5d21232d37cde685a79531a777a60151f617a..df2313d1d03ada2fabb80c8a050cf5de4028eab9 100644
--- a/src/main/java/neatlogic/framework/common/config/Config.java
+++ b/src/main/java/neatlogic/framework/common/config/Config.java
@@ -236,7 +236,7 @@ public class Config {
return DB_URL;
}
- public static String DB_TRANSACTION_TIMEOUT() {
+ public static String DB_TRANSACTION_TIMEOUT() {// root-context.xml中使用了该变量
return DB_TRANSACTION_TIMEOUT;
}
diff --git a/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml b/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml
index 0716ccb57af835c5a937e0357649c9ba508a3ba1..d8127410ff870420393a5056fe83ad87034afad1 100644
--- a/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml
+++ b/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml
@@ -32,12 +32,15 @@ along with this program. If not, see .-->
+
+
-
-
-
-
-
+
+
+
+
+
+
diff --git a/src/main/java/neatlogic/framework/dao/plugin/DataSchemaInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/DataSchemaInterceptor.java
index 761374c78b8d85f0786a6fbf97acfd866aa5b94d..7ff22cf0c043c7b2e7624c84237442474e8af779 100644
--- a/src/main/java/neatlogic/framework/dao/plugin/DataSchemaInterceptor.java
+++ b/src/main/java/neatlogic/framework/dao/plugin/DataSchemaInterceptor.java
@@ -31,12 +31,10 @@ import java.sql.Connection;
*/
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataSchemaInterceptor implements Interceptor {
- // 判断是否查询了数据库
- public static final ThreadLocal QUERY_FROM_DATABASE_INSTANCE = new ThreadLocal<>();
@Override
public Object intercept(Invocation invocation) throws Throwable {
- QUERY_FROM_DATABASE_INSTANCE.set(true);
+ SqlCostInterceptor.QUERY_FROM_DATABASE_INSTANCE.set(true);
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
diff --git a/src/main/java/neatlogic/framework/dao/plugin/ExceptionCatchInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ExceptionCatchInterceptor.java
index 1b656a770b9ee12b7baae041e8d114dc6a5db9d5..91d8279476372a34b8c6a1477313c7d37b4c5b26 100644
--- a/src/main/java/neatlogic/framework/dao/plugin/ExceptionCatchInterceptor.java
+++ b/src/main/java/neatlogic/framework/dao/plugin/ExceptionCatchInterceptor.java
@@ -109,12 +109,13 @@ public class ExceptionCatchInterceptor implements Interceptor {
Object parameterObject = invocation.getArgs()[1];
logger.error("The error may exist in {}", ms.getResource());
logger.error("The error may involve {} -Inline", ms.getId());
- logger.error("SQL: {}", ms.getBoundSql(parameterObject).getSql());
+ logger.error("SQL: {}", SqlCostInterceptor.getSql(ms, parameterObject));
logger.error("parameters: {}", JSON.toJSONString(parameterObject));
logger.error(targetException.getMessage(), targetException);
}
Object parameterObject = invocation.getArgs()[1];
- defaultLogger.error("SQL Failed: {} with params: {}", ms.getBoundSql(parameterObject).getSql(), JSON.toJSONString(parameterObject), targetException);
+ String sql = SqlCostInterceptor.getSql(ms, parameterObject);
+ defaultLogger.error("SQL Failed: {}: {} with params: {}", ms.getId(), sql, JSON.toJSONString(parameterObject), targetException);
throw targetException;
}
return result;
diff --git a/src/main/java/neatlogic/framework/dao/plugin/ModifyResultMapTypeHandlerInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ModifyResultMapTypeHandlerInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..436fbfe7d01db925488e67f250c67988cfe356bb
--- /dev/null
+++ b/src/main/java/neatlogic/framework/dao/plugin/ModifyResultMapTypeHandlerInterceptor.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2025 深圳极向量科技有限公司 All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package neatlogic.framework.dao.plugin;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.ibatis.executor.resultset.ResultSetHandler;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.ResultMap;
+import org.apache.ibatis.mapping.ResultMapping;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.plugin.Intercepts;
+import org.apache.ibatis.plugin.Invocation;
+import org.apache.ibatis.plugin.Signature;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.StringTypeHandler;
+import org.apache.ibatis.type.TypeHandler;
+import org.apache.ibatis.type.TypeHandlerRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+
+@Intercepts({
+ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
+})
+public class ModifyResultMapTypeHandlerInterceptor implements Interceptor {
+
+ Logger logger = LoggerFactory.getLogger(ModifyResultMapTypeHandlerInterceptor.class);
+ public static final ThreadLocal mappedStatementThreadLocal = new ThreadLocal<>();
+
+ @Override
+ public Object intercept(Invocation invocation) throws Throwable {
+ try {
+ MappedStatement mappedStatement = mappedStatementThreadLocal.get();
+ if (mappedStatement != null) {
+ Configuration configuration = mappedStatement.getConfiguration();
+ int resultMappingSize = 0;
+ List resultMaps = mappedStatement.getResultMaps();
+ for (ResultMap resultMap : resultMaps) {
+ resultMappingSize += resultMap.getResultMappings().size();
+ }
+ if (resultMappingSize > 0) {
+ List longVarcharColumnLabelList = new ArrayList<>();
+ PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];
+ ResultSet rs = ps.getResultSet();
+ final ResultSetMetaData metaData = rs.getMetaData();
+ final int columnCount = metaData.getColumnCount();
+ for (int i = 1; i <= columnCount; i++) {
+ int columnType = metaData.getColumnType(i);
+ JdbcType jdbcType = JdbcType.forCode(columnType);
+ if (jdbcType == JdbcType.LONGVARCHAR) {
+ String columnLabel = metaData.getColumnLabel(i);
+ longVarcharColumnLabelList.add(columnLabel);
+ }
+ }
+ if (CollectionUtils.isNotEmpty(longVarcharColumnLabelList)) {
+ for (ResultMap resultMap : resultMaps) {
+ handleResultMap(longVarcharColumnLabelList, configuration, resultMap);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ } finally {
+ mappedStatementThreadLocal.remove();
+ }
+ return invocation.proceed();
+ }
+
+ private void handleResultMap(List longVarcharColumnLabelList, Configuration configuration, ResultMap resultMap) throws NoSuchFieldException, IllegalAccessException {
+ List resultMappings = resultMap.getResultMappings();
+ if (CollectionUtils.isNotEmpty(resultMappings)) {
+ TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
+ for (ResultMapping resultMapping : resultMappings) {
+ if (longVarcharColumnLabelList.contains(resultMapping.getColumn())) {
+ TypeHandler> typeHandler = resultMapping.getTypeHandler();
+ if (typeHandler instanceof StringTypeHandler) {
+ Field typeHandlerField = resultMapping.getClass().getDeclaredField("typeHandler");
+ typeHandlerField.setAccessible(true);
+ typeHandlerField.set(resultMapping, typeHandlerRegistry.getTypeHandler(String.class, JdbcType.LONGVARCHAR));
+ }
+ } else {
+ String nestedResultMapId = resultMapping.getNestedResultMapId();
+ if (nestedResultMapId != null) {
+ handleResultMap(longVarcharColumnLabelList, configuration, configuration.getResultMap(nestedResultMapId));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/neatlogic/framework/dao/plugin/SqlCostInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/SqlCostInterceptor.java
index fd678ec7571e46f324b04a89a7e259d618760617..7052d0e40dfaedfe1d58ba3053c8a9ece6490580 100644
--- a/src/main/java/neatlogic/framework/dao/plugin/SqlCostInterceptor.java
+++ b/src/main/java/neatlogic/framework/dao/plugin/SqlCostInterceptor.java
@@ -51,6 +51,8 @@ import java.util.regex.Matcher;
})
public class SqlCostInterceptor implements Interceptor {
Logger logger = LoggerFactory.getLogger(SqlCostInterceptor.class);
+ // 判断是否查询了数据库
+ public static final ThreadLocal QUERY_FROM_DATABASE_INSTANCE = new ThreadLocal<>();
public static class SqlIdMap {
private static final Set sqlSet = new HashSet<>();
@@ -91,13 +93,14 @@ public class SqlCostInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
+ MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
+ ModifyResultMapTypeHandlerInterceptor.mappedStatementThreadLocal.set(mappedStatement);
long starttime = 0;
SqlAuditVo sqlAuditVo = null;
boolean hasCacheFirstLevel = false;
try {
if (!SqlIdMap.isEmpty()) {
// Object target = invocation.getTarget();
- MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
String sqlId = mappedStatement.getId(); // 获取到节点的id,即sql语句的id
if (SqlIdMap.isExists(sqlId)) {
sqlAuditVo = new SqlAuditVo();
@@ -114,9 +117,7 @@ public class SqlCostInterceptor implements Interceptor {
parameter = invocation.getArgs()[1];
}
- BoundSql boundSql = mappedStatement.getBoundSql(parameter); // BoundSql就是封装myBatis最终产生的sql类
- Configuration configuration = mappedStatement.getConfiguration(); // 获取节点的配置
- String sql = getSql(configuration, boundSql, sqlId); // 获取到最终的sql语句
+ String sql = getSql(mappedStatement, parameter); // 获取到最终的sql语句
//System.out.println("#############################SQL INTERCEPTOR###############################");
//System.out.println("id:" + sqlId);
//System.out.println(sql);
@@ -142,39 +143,53 @@ public class SqlCostInterceptor implements Interceptor {
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
- DataSchemaInterceptor.QUERY_FROM_DATABASE_INSTANCE.set(false);
- // 执行完上面的任务后,不改变原有的sql执行过程
- Object val = invocation.proceed();
- if (sqlAuditVo != null) {
- if (DataSchemaInterceptor.QUERY_FROM_DATABASE_INSTANCE.get()) {
- // sql语句被执行,没有使用到缓存
- sqlAuditVo.setUseCacheLevel(StringUtils.EMPTY);
- } else {
- if (hasCacheFirstLevel) {
- sqlAuditVo.setUseCacheLevel("一级缓存");
+ QUERY_FROM_DATABASE_INSTANCE.set(false);
+ try {
+ // 执行完上面的任务后,不改变原有的sql执行过程
+ Object val = invocation.proceed();
+ if (sqlAuditVo != null) {
+ if (QUERY_FROM_DATABASE_INSTANCE.get()) {
+ // sql语句被执行,没有使用到缓存
+ sqlAuditVo.setUseCacheLevel(StringUtils.EMPTY);
} else {
- sqlAuditVo.setUseCacheLevel("二级缓存");
+ if (hasCacheFirstLevel) {
+ sqlAuditVo.setUseCacheLevel("一级缓存");
+ } else {
+ sqlAuditVo.setUseCacheLevel("二级缓存");
+ }
}
- }
- sqlAuditVo.setTimeCost(System.currentTimeMillis() - starttime);
- sqlAuditVo.setRunTime(new Date());
+ sqlAuditVo.setTimeCost(System.currentTimeMillis() - starttime);
+ sqlAuditVo.setRunTime(new Date());
- if (val != null) {
- if (val instanceof List) {
- sqlAuditVo.setRecordCount(((List) val).size());
- } else {
- sqlAuditVo.setRecordCount(1);
+ if (val != null) {
+ if (val instanceof List) {
+ sqlAuditVo.setRecordCount(((List) val).size());
+ } else {
+ sqlAuditVo.setRecordCount(1);
+ }
}
+ SqlAuditManager.addSqlAudit(sqlAuditVo);
+ RequestContext requestContext = RequestContext.get();
+ if (requestContext != null) {
+ requestContext.addSqlAudit(sqlAuditVo);
+ }
+ //System.out.println("time cost:" + (System.currentTimeMillis() - starttime) + "ms");
+ //System.out.println("###########################################################################");
}
- SqlAuditManager.addSqlAudit(sqlAuditVo);
- RequestContext requestContext =RequestContext.get();
- if (requestContext != null) {
- requestContext.addSqlAudit(sqlAuditVo);
- }
- //System.out.println("time cost:" + (System.currentTimeMillis() - starttime) + "ms");
- //System.out.println("###########################################################################");
+ return val;
+ } finally {
+ QUERY_FROM_DATABASE_INSTANCE.remove();
+ }
+ }
+
+ public static String getSql(MappedStatement mappedStatement, Object parameterObject) {
+ Configuration configuration = mappedStatement.getConfiguration();
+ BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
+ String sql = showSql(configuration, boundSql);
+ if (sql.contains("@{DATA_SCHEMA}")) {
+ sql = sql.replace("@{DATA_SCHEMA}", TenantContext.get().getDataDbName());
}
- return val;
+ return sql;
}
// 封装了一下sql语句,使得结果返回完整xml路径下的sql语句节点id + sql语句