iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >Mybatis 插件: MySQL sql 语句转换为合法的达梦sql语句
  • 301
分享到

Mybatis 插件: MySQL sql 语句转换为合法的达梦sql语句

mybatismysqlsql 2023-09-23 15:09:30 301人浏览 泡泡鱼
摘要

        目录 问题 分析 测试 算法 总结 问题          因为现在提倡使用国产化数据库,而且客户也有信创的要求,所以要把项目使用国产化数据库进行部署。项目已经运行了很多年了,里面有大量的 sql 语句,底层数据库都是

       

目录

问题

分析

测试

算法

总结


问题

         因为现在提倡使用国产化数据库,而且客户也有信创的要求,所以要把项目使用国产化数据库进行部署。项目已经运行了很多年了,里面有大量的 sql 语句,底层数据库都是 Mysql,现在要迁移到达梦数据库,怎么办?一开始的解决方案是这样的,先拷贝一份 mapper xml 文件, 再在上面改 SQL ,SQL 功能逻辑不变,可能就是小修小改, SQL 语句无非就是增删改查,而且大多数都是单表的增删改查,只是多费一些时间而已,但怎么知道这个 SQL 语句要不要改?怎么改?改了后怎么测试它是正确的?仅凭肉眼观察而不用实际运行一下?还是改一个 mapper 就测试一下?很多人都熟悉 mysql 但不熟悉达梦,这时候就要开始去看别人踩过的坑,下载一份官方手册,对照着看,估计一下工作量。

        粗略看了一下 mapper 文件的数量,整个项目 20 多个模块一共 600 多个mapper 文件,这些 mapper xml 都是不同的人不同时期写的,什么 “replace into”,“on duplicate key update ”,“insert ignore into”,第一次看到 MySQL 还能这样写,前两者在达梦中要转为 “merge into”,还有很多函数是 MySQL 里有但达梦没有,还需要找替换方法的,有些函数是 MySQL 和 达梦都有但参数写法不同的,这些一开始就能想到的,还有一些看上去没问题,结果在 SQL 测试报错了才知道不行,算是暗坑,比如 “left join tblName where xxx” 在 MySQL 中没问题,但在达梦中就会报错,要改为 “left join tblName on true where xxx”,我之前都没见过更没想到还有 “left join tblName where xxx” 这种写法,还有 group by,having 不支持别名,“comment,ref”关键字报错等等问题。最后我考虑了一下就放弃把每个 mapper xml 文件都手动改,手动测的方案,改为用 mybatis 插件的方式做,用代码的方式自动化调整 SQL 语句。把达梦数据库实例设置为兼容 MySQL 模式就可以兼容大部分的 MySQL 语法,减少工作量。

分析

        Mybatis 拦截器有 4 个入口可以进行拦截,但我们拦截的目的也就是改写 SQL 字符串,在 Statement 层进行拦截就好了,这时候获取到的 SQL 字符串有 “?”,它已经处理完 , 这些动态标签了,此时的 SQL 字符串叫“静态 SQL ”。在这个过程中必须要保证原始 sql 中的每一个 “?” 和 ParameterMapping 对象位置顺序,数量一一对应,不能有任何的变动。如果你改写 SQL 后多加一个“?”,就必须在 ParameterMapping 列表和这个“?”的相同位置处添加一个代表这个“?” 暗示的ParameterMapping ,否则就会报错。这个和 Mybatis 中“?”和 ParameterMapping 列表的生成有关。 在改写 SQL 字符串的时候,必须保证修改后的 SQL 和之前的 SQL 功能一样,而且不对其他不相干的 SQL 有影响,可以理解为其他不相干的 SQL 都是这个改写逻辑方法的测试用例。写完插件,测完代码后,我总结了需要改的 3 种类型:

        第 1 种就是方法改写,比如 str_to_date() 方法要改为 date_fORMat() 方法,它里面的参数位置顺序可能需要调整;

        第 2 种就是插入,更新的写法转换,比如  “replace into” 的写法改为 “merge into” 的写法;

比如

replace into test(id,numbers,age) values(2,100,15)

要改为

merge into A.testusing (select 2 id,100 numbers,15 age from dual) t on(A.test.id = t.id) when matched then update set A.test.numbers=t.numbers,A.test.age=t.agewhen not matched then insert (id,numbers,age) values(t.id,t.numbers,t.age)

        第 3 种是 select 类型,比如 group by ,having 别名改为原本的样子,

select count(*) cnt from tbl where xxx group by cnt

要改为 

select count(*) cnt from tbl where xxx group by count(*)

这三种类型分别使用责任链设计模式可以得到很好处理。

for (FunctionTransfer functionTransfer : functionTransferList) {    if (functionTransfer.isSupport(sql)) {        sql = functionTransfer.transfer(sql);    }}

测试

        通过反射 mapper 接口代理对象执行全部的接口方法,发现 SQL 报错,找到 SQL 报错原因,把修改后正确的逻辑自动化为代码,并做严格的限制,使它不影响到其他的代码的执行。有的长 SQL 有 500多行,空格,括号一层套一层,我们可以通过一些简单的算法提取出我们想要的字符串,并进行处理,在这个过程中用到了两个力扣上的算法,写过就会了。

算法

 1,获取一个字符串列表,列表元素可能包含嵌套括号,以“,”作为分隔符。

public static List getSegment(String sql, int left, int right) {        List list = new ArrayList<>();        int leftFlag = 0, j = left, start = left;        while (j < right) {            if (sql.charAt(j) == ',' && leftFlag == 0) {                list.add(sql.substring(start, j).trim());                start = j + 1;            }            if (sql.charAt(j) == '(') {                leftFlag++;            }            if (sql.charAt(j) == ')') {                leftFlag--;            }            if (j == right - 1) {                String substr = sql.substring(start, right).trim();                if (substr.length() > 0) {                    list.add(substr);                }            }            j++;        }        return list;    }

2,递归解决嵌套问题,把嵌套 if() 转为 嵌套 case when。因为 if() 里面可能嵌套 if(),为什么要把 if() 转为 case when? 因为达梦中的 if() 使用受限,和 MySQL 中的 if() 不一样。另外只要有分支嵌套,都可以考虑使用递归的方式解决。还有一个地方也是使用这种方式解决嵌套问题,但还要考虑很多的其他原因。

public String getTrans(String sql) {        int idx = sql.indexOf("if(");        while (idx >= 0) {            StringBuilder sb = new StringBuilder();            Content expectContent = StrUtil.getExpectContent(sql, idx + 2);            assert expectContent != null;            String expectStr = expectContent.getExpectStr();            int end = expectContent.getEnd();            List segmentList = StrUtil.getSegment(expectStr, 0, expectStr.length());            String whenStr = segmentList.get(0);            // if() 函数里的 whenStr,trueStr,falseStr 都可能继续有 if() 函数,所以需要递归执行,            // 要把所有的嵌套 if() 都变为嵌套的 case when。在分支选择判断的地方最容易出现嵌套,比如 if(),case when,而            // 其他的普通函数则很少有嵌套,即使有,也很容易通过迭代的方式遍历出来,因为普通函数就只有一个执行分支,比如            // date_format(date_format('2012-12-12 12:12:12','%Y-%m-%m'),'%Y'),要么执行成功,要么执行失败。            if (whenStr.contains("if(")) {                whenStr = "(" + getTrans(whenStr) + ")";            }            String trueStr = segmentList.get(1);            if (trueStr.contains("if(")) {                trueStr = "(" + getTrans(trueStr) + ")";                //  if(num1 != null,num2 != null,num3) 的解决方法            } else if (trueStr.contains("!=") && trueStr.split("!=")[1].trim().equalsIgnoreCase("null")) {                String[] split = trueStr.split("!=");                trueStr = "ifnull(" + split[0] + ",0)";            }            String falseStr = segmentList.get(2);            if (falseStr.contains("if(")) {                falseStr = "(" + getTrans(falseStr) + ")";            }            sql = sb.append(sql, 0, idx).append(" case when ").append(whenStr).append(" then ").append(trueStr).append(" else ")                    .append(falseStr).append(" end ").append(sql.substring(end)).toString();            idx = sql.indexOf("if(", idx + 3);        }        return sql;    }

总结

        最耗时的部分在于测试,因为是老项目,里面的 SQL 很多都是自动生成的,很多 SQL 语句已经废弃了,都不删,也没有接口测试,我只能假定所有 SQL 都是正确,全都执行一遍,结果浪费了很多时间,而且有个模块有 180 多个文件,启动一次要花60多秒。发现报错,解决报错,写代码的时间反而不是很多。

        这个插件核心代码大概 2 千多行,测试代码有1千多行。写的时候不知道有 SQL 解析库,很多字符串操作都是靠正则表达式来完成,后面才知道有 jsqlParser 这个强大的 SQL 解析库,通过访问者模式可以操作任意一个合法的 SQL ,因为它把 SQL 进行了语法解析和词法解析,让人事半功倍,真是磨刀不误砍柴工。

        后续:使用 JSqlParser 库解析 sql,但很多正确的 sql JSqlParser 并不支持,无奈之下弃用 JSqlParser 库转用 druid 库,几乎完美支持 MySQL 的语法,但在解析的时候发现 SQLExpr 表达式的 toString() 方法并不好,只能通过递归的方法去获取正确的 toString() 方法,写完后很放心了。

来源地址:https://blog.csdn.net/qq_38148090/article/details/130651854

您可能感兴趣的文档:

--结束END--

本文标题: Mybatis 插件: MySQL sql 语句转换为合法的达梦sql语句

本文链接: https://www.lsjlt.com/news/416511.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Mybatis 插件: MySQL sql 语句转换为合法的达梦sql语句
            目录 问题 分析 测试 算法 总结 问题          因为现在提倡使用国产化数据库,而且客户也有信创的要求,所以要把项目使用国产化数据库进行部署。项目已经运行了很多年了,里面有大量的 SQL 语句,底层数据库都是 ...
    99+
    2023-09-23
    mybatis mysql sql
  • MyBatis sql语句从mysql转换为Oracle
    MyBatis中将SQL语句从MySQL转换为Oracle,主要涉及以下几个方面: 语法差异 MySQL和Oracle的SQL语法有一定差异,需要进行对应的转换,比如:- LIMIT -> ROWNUM - AUTO_INCREMENT ...
    99+
    2023-09-20
    mybatis oracle sql
  • sql插入语句的基本语法
    小编给大家分享一下sql插入语句的基本语法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!在SQL中我们可以使用insert in...
    99+
    2024-04-02
  • SQL行转列合并的语句
    这篇文章主要讲解了“SQL行转列合并的语句”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SQL行转列合并的语句”吧!   SQL> cr...
    99+
    2024-04-02
  • mysql行转列的sql语句怎么写
    在MySQL中,可以使用以下方式将行转列: 使用CASE语句和聚合函数: SELECT id, MAX(CASE...
    99+
    2024-04-09
    mysql
  • 怎么用sql语句将时间戳转换为日期
    在SQL中,可以使用DATE函数将时间戳转换为日期。具体的方法如下: SELECT DATE(FROM_UNIXTIME(t...
    99+
    2023-10-24
    sql
  • Mybatis控制台打印SQL语句的方法
    小编给大家分享一下Mybatis控制台打印SQL语句的方法,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!问题描述在使用mybatis进行开发的时候,由于可以动态拼接sql,这样大大方便了我们。但是也有一定的问题,当我们动态...
    99+
    2023-06-14
  • mybatis中sql语句CDATA标签的用法说明
    sql语句CDATA标签的用法 CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)。 在 XML 元素中,"<" 和 ...
    99+
    2024-04-02
  • mybatis中sql语句CDATA标签的用法介绍
    这篇文章主要介绍“mybatis中sql语句CDATA标签的用法介绍”,在日常操作中,相信很多人在mybatis中sql语句CDATA标签的用法介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”mybatis...
    99+
    2023-06-20
  • MyBatis控制台显示SQL语句的方法实现
    一、单独使用MyBatis (1)在mybatis.xml配置文件中添加如下配置 <setting name="logImpl" value="STDOUT_LOGGING...
    99+
    2024-04-02
  • sql条件查询语句的实例用法
    本篇内容主要讲解“sql条件查询语句的实例用法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“sql条件查询语句的实例用法”吧! 复制...
    99+
    2024-04-02
  • 如何使用SQL语句在MySQL中进行数据转换和转移?
    在MySQL中进行数据转换和转移是一个常见的任务。这种任务有很多种不同的方法,其中最常见的方法是使用SQL语句。本文将介绍如何使用SQL语句在MySQL中进行数据转换和转移,并提供具体的代码示例。一、数据转换数据转换是将一个或多个数据类型转...
    99+
    2023-12-17
    MySQL 数据转换 数据转换:SQL语句 数据转移:SQL语句
  • mybatis-plus 使用Condition拼接Sql语句各方法的用法
    mybatis-plus Condition拼接Sql语句各方法 1.setSqlSelect—用于添加查询的列信息 public Wrapper<T> s...
    99+
    2024-04-02
  • 怎么使用Java校验SQL语句的合法性
    这篇文章主要介绍“怎么使用Java校验SQL语句的合法性”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么使用Java校验SQL语句的合法性”文章能帮助大家解决问题。方案一:使用JDBC API中提...
    99+
    2023-07-05
  • MySQL中正则表达式查询的SQL语句都有哪些
    本篇文章给大家分享的是有关MySQL中正则表达式查询的SQL语句都有哪些,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。MySQL作为关系型数据...
    99+
    2024-04-02
  • MySQL插入SQL语句后在phpmyadmin中注释乱码的解决方法
    这篇文章将为大家详细讲解有关MySQL插入SQL语句后在phpmyadmin中注释乱码的解决方法,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。MySQL插入SQL语句后在...
    99+
    2024-04-02
  • MySQL常用SQL语句在MongoDB中的写法有哪些
    MySQL常用SQL语句在MongoDB中的写法有哪些,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。  如果你长期使用MySQ...
    99+
    2024-04-02
  • mysql sql语句将查出来的每列变为字符串类型
    在MySQL中,可以使用CAST()函数或CONVERT()函数将查出来的每列数据转换为字符串类型。具体用法如下: 使用CAST()函数: SELECT CAST(column_name AS CHAR) AS new_column...
    99+
    2023-08-25
    mysql 数据库
  • 使用Java校验SQL语句的合法性五种解决方案
    方案一:使用JDBC API中提供的Statement接口的execute()方法 要在Java中校验SQL语句的合法性,可以使用JDBC API中提供的Statement接口的ex...
    99+
    2023-05-14
    java校验sql语句 java校验sql java校验
  • Mybatis注解方式完成输入参数为list的SQL语句拼接方式
    目录Mybatis注解完成输入参数为list的SQL语句拼接拼接查询条件为list集合的sql函数Mybatis注解完成输入参数为list的SQL语句拼接 首先将list集合拼接成一...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作