From c90ab1a10238a61878f1bb029681283d41377764 Mon Sep 17 00:00:00 2001 From: tangw Date: Wed, 12 Aug 2026 10:08:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=95=B0=E6=8D=AE=E6=BA=90?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lis-system/pom.xml | 13 +- .../system/config/MyBatisPluginConfig.java | 29 ++++ .../SqlPlusToConcatInterceptor.java | 153 ++++++++++++++++++ pom.xml | 19 ++- .../main/resources/mapper/ZybgcxMapper.xml | 4 +- 5 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 lis-system/src/main/java/com/czlis/system/config/MyBatisPluginConfig.java create mode 100644 lis-system/src/main/java/com/czlis/system/interceptor/SqlPlusToConcatInterceptor.java diff --git a/lis-system/pom.xml b/lis-system/pom.xml index cabec75..170fa69 100644 --- a/lis-system/pom.xml +++ b/lis-system/pom.xml @@ -116,7 +116,18 @@ com.microsoft.sqlserver mssql-jdbc - + + cn.com.kingbase + kingbase8 + + + com.dameng + DmJdbcDriver8 + + + com.github.jsqlparser + jsqlparser + org.springframework.boot spring-boot-starter-test diff --git a/lis-system/src/main/java/com/czlis/system/config/MyBatisPluginConfig.java b/lis-system/src/main/java/com/czlis/system/config/MyBatisPluginConfig.java new file mode 100644 index 0000000..2e1f7c4 --- /dev/null +++ b/lis-system/src/main/java/com/czlis/system/config/MyBatisPluginConfig.java @@ -0,0 +1,29 @@ +package com.czlis.system.config; + +import com.czlis.system.interceptor.SqlPlusToConcatInterceptor; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; +import javax.annotation.Resource; +import java.util.Map; + +@Configuration +public class MyBatisPluginConfig { + + @Resource + private ApplicationContext applicationContext; + + @Bean + public ApplicationListener registerInterceptor() { + return event -> { + Map map = applicationContext.getBeansOfType(SqlSessionFactory.class); + SqlPlusToConcatInterceptor interceptor = new SqlPlusToConcatInterceptor(); + for (SqlSessionFactory sf : map.values()) { + sf.getConfiguration().addInterceptor(interceptor); + } + }; + } +} \ No newline at end of file diff --git a/lis-system/src/main/java/com/czlis/system/interceptor/SqlPlusToConcatInterceptor.java b/lis-system/src/main/java/com/czlis/system/interceptor/SqlPlusToConcatInterceptor.java new file mode 100644 index 0000000..b535557 --- /dev/null +++ b/lis-system/src/main/java/com/czlis/system/interceptor/SqlPlusToConcatInterceptor.java @@ -0,0 +1,153 @@ +package com.czlis.system.interceptor; + +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.plugin.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * SQLServer转达梦 MyBatis SQL兼容拦截器 + * 全部正则模式,不依赖JSqlParser解析器,解决低版本jar包类缺失问题 + * 注意:该模式无法区分数字相加和字符串拼接,SQL尽量不要同时出现数字+运算 + */ +@Intercepts({ + @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) +}) +public class SqlPlusToConcatInterceptor implements Interceptor { + + private static final Logger log = LoggerFactory.getLogger(SqlPlusToConcatInterceptor.class); + + // '常量'+col 或者 col+'常量' + private static final Pattern CONCAT_PATTERN = Pattern.compile("(\\s*'[^']*'\\s*\\+|\\+\\s*'[^']*')"); + + // CHARINDEX(a,b) + private static final Pattern CHARINDEX_PATTERN = Pattern.compile("CHARINDEX\\s*\\(\\s*(.+?)\\s*,\\s*(.+?)\\s*\\)", Pattern.CASE_INSENSITIVE); + + // ISNULL(a,b) + private static final Pattern ISNULL_2PARAM_PATTERN = Pattern.compile("ISNULL\\s*\\(\\s*(.+?)\\s*,\\s*(.+?)\\s*\\)", Pattern.CASE_INSENSITIVE); + + // RIGHT(REPLICATE(' ',N)+expr,N) + private static final Pattern RIGHT_REPLICATE_PATTERN = Pattern.compile( + "RIGHT\\s*\\(\\s*REPLICATE\\s*\\(\\s*'\\s*'\\s*,\\s*(\\d+)\\s*\\)\\s*\\+\\s*(.+?)\\s*,\\s*(\\d+)\\s*\\)", + Pattern.CASE_INSENSITIVE | Pattern.DOTALL + ); + + // REPLICATE(str,N) + private static final Pattern REPLICATE_PATTERN = Pattern.compile( + "REPLICATE\\s*\\(\\s*(.+?)\\s*,\\s*(\\d+)\\s*\\)", + Pattern.CASE_INSENSITIVE | Pattern.DOTALL + ); + + @Override + public Object intercept(Invocation invocation) throws Throwable { + log.info("====进入SQL兼容拦截器===="); + StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); + BoundSql boundSql = statementHandler.getBoundSql(); + String originSql = boundSql.getSql(); + Connection connection = (Connection) invocation.getArgs()[0]; + + DatabaseMetaData metaData = connection.getMetaData(); + String productName = metaData.getDatabaseProductName(); + boolean enableConvert = productName != null && productName.toUpperCase().contains("DM"); + log.info("识别数据库产品名称:[{}], 是否开启SQL兼容转换:{}", productName, enableConvert); + + try { + if (!enableConvert) { + return invocation.proceed(); + } + + String lowerSql = originSql.trim().toLowerCase(); + if (lowerSql.startsWith("call ") || lowerSql.startsWith("exec ")) { + return invocation.proceed(); + } + + String targetSql = originSql; + StringBuffer sb = new StringBuffer(); + + //1. RIGHT(REPLICATE(' ',N)+expr,N) → SUBSTR(LPAD(expr,N,' '),1,N) + Matcher mRightRep = RIGHT_REPLICATE_PATTERN.matcher(targetSql); + sb.setLength(0); + while (mRightRep.find()) { + String num = mRightRep.group(1).trim(); + String expr = mRightRep.group(2).trim(); + String newExpr = String.format("SUBSTR(LPAD(%s,%s,' '),1,%s)", expr, num, num); + mRightRep.appendReplacement(sb, Matcher.quoteReplacement(newExpr)); + } + mRightRep.appendTail(sb); + targetSql = sb.toString(); + + //2. 'xxx' + col / col + 'xxx' 加号替换为 || + Matcher mConcat = CONCAT_PATTERN.matcher(targetSql); + sb.setLength(0); + while (mConcat.find()) { + String found = mConcat.group(); + mConcat.appendReplacement(sb, Matcher.quoteReplacement(found.replace("+", "||"))); + } + mConcat.appendTail(sb); + targetSql = sb.toString(); + + //3. CHARINDEX(a,b) → INSTR(b,a) + Matcher mCharIndex = CHARINDEX_PATTERN.matcher(targetSql); + sb.setLength(0); + while (mCharIndex.find()) { + String p1 = mCharIndex.group(1).trim(); + String p2 = mCharIndex.group(2).trim(); + String newFunc = String.format("INSTR(%s,%s)", p2, p1); + mCharIndex.appendReplacement(sb, Matcher.quoteReplacement(newFunc)); + } + mCharIndex.appendTail(sb); + targetSql = sb.toString(); + + //4. ISNULL(a,b) → NVL(a,b) + Matcher mIsnull = ISNULL_2PARAM_PATTERN.matcher(targetSql); + sb.setLength(0); + while (mIsnull.find()) { + String p1 = mIsnull.group(1).trim(); + String p2 = mIsnull.group(2).trim(); + String newFunc = String.format("NVL(%s,%s)", p1, p2); + mIsnull.appendReplacement(sb, Matcher.quoteReplacement(newFunc)); + } + mIsnull.appendTail(sb); + targetSql = sb.toString(); + + //5. REPLICATE(str,N) → REPEAT(str,N) + Matcher mRep = REPLICATE_PATTERN.matcher(targetSql); + sb.setLength(0); + while (mRep.find()) { + String src = mRep.group(1).trim(); + String cnt = mRep.group(2).trim(); + String newFunc = String.format("REPEAT(%s,%s)", src, cnt); + mRep.appendReplacement(sb, Matcher.quoteReplacement(newFunc)); + } + mRep.appendTail(sb); + targetSql = sb.toString(); + + //回填修改后的SQL + if (!originSql.equals(targetSql)) { + log.debug("【SQL兼容转换】\n原SQL:\n{}\n转换后:\n{}", originSql, targetSql); + org.apache.ibatis.reflection.MetaObject metaObject + = org.apache.ibatis.reflection.SystemMetaObject.forObject(statementHandler); + metaObject.setValue("delegate.boundSql.sql", targetSql); + } + } catch (Exception e) { + log.warn("【SQL转换处理异常,直接放行原始SQL】sql:{} , ex:{}", originSql, e.getMessage()); + } + return invocation.proceed(); + } + + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + @Override + public void setProperties(Properties properties) { + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 206eccd..f194409 100644 --- a/pom.xml +++ b/pom.xml @@ -308,7 +308,24 @@ mssql-jdbc ${sqlserver.version} - + + + cn.com.kingbase + kingbase8 + 8.2.0 + + + + com.dameng + DmJdbcDriver8 + 8.1.5.45 + + + + com.github.jsqlparser + jsqlparser + 4.6 + com.belerweb diff --git a/zybgcx/src/main/resources/mapper/ZybgcxMapper.xml b/zybgcx/src/main/resources/mapper/ZybgcxMapper.xml index e3b6b3f..7f7b93c 100644 --- a/zybgcx/src/main/resources/mapper/ZybgcxMapper.xml +++ b/zybgcx/src/main/resources/mapper/ZybgcxMapper.xml @@ -6,7 +6,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"