Mybatis动态解析XML字符串

1. 下面的工具类是传入 一个 类似于mybatisXML 写法的 sql 字符串对应的参数 ,获得一个 解析好的最终sql 。

工具类如下:

package org.example.demo.util;

import com.alibaba.fastjson.JSON;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.scripting.xmltags.XMLScriptBuilder;
import org.apache.ibatis.session.Configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.StringReader;
import java.util.List;
import java.util.UUID;



public class MyBatisUtil {
    private static final Logger log = LoggerFactory.getLogger(MyBatisUtil.class);
    private static final Configuration configuration = new Configuration();
    private static DocumentBuilder documentBuilder;

    static {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(false);
        try {
            documentBuilder = dbf.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
    }


    /**
     * 该方法主要用来解析动态sql,可以使用mybatis的所有标签
     * 解析和赋值的方式都是由mybatis 完成的
     * 赋值绑定几乎完全使用该类 {@link  org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters(java.sql.PreparedStatement)}
     *
     * @param xmlSQL          eg:  <select> mybatisXML sql 语句</select>
     * @param parameterObject 对应的参数
     * @return 解析后的sql 语句
     */
    public static String parseDynamicXMLFormXmlStr(String xmlSQL, Object parameterObject) {

        log.info("原始sqlXml:{} , params:{}", xmlSQL, JSON.toJSONString(parameterObject));
        //解析成xml
        Document doc = parseXMLDocument(xmlSQL);
        if (doc == null) {
            return null;
        }
        //走mybatis 流程 parse成Xnode
        XNode xNode = new XNode(new XPathParser(doc, false), doc.getFirstChild(), null);
        // 之前的所有步骤 都是为了构建 XMLScriptBuilder 对象,
        XMLScriptBuilder xmlScriptBuilder = new XMLScriptBuilder(configuration, xNode);

        //解析 静态xml 和动态的xml
        SqlSource sqlSource = xmlScriptBuilder.parseScriptNode();
        MappedStatement ms = new MappedStatement.Builder(configuration, UUID.randomUUID().toString(), sqlSource, null).build();

        //将原始sql 与 参数绑定
        BoundSql boundSql = ms.getBoundSql(parameterObject);

        //获得 预编译后的 sql
        String resultSql = boundSql.getSql();
        //将'  ?  '和"  ?  " 替换为 ?
        String executeSql = resultSql.replaceAll("(\'\\s*\\?\\s*\')|(\"\\s*\\?\\s*\")", "?");
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    Object value;
                    String propertyName = parameterMapping.getProperty();
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else {
                        MetaObject metaObject = configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                    executeSql = executeSql.replaceFirst("[?]", value instanceof String ? "'" + value + "'" : String.valueOf(value));
                }
            }
        }
        //格式化 sql 移除多余空格
        log.info("removeExtraWhitespace -> executeSql: {}", SqlSourceBuilder.removeExtraWhitespaces(executeSql));
        return executeSql;
    }


    private static Document parseXMLDocument(String xmlString) {
        if (StringUtils.isBlank(xmlString)) {
            log.error("动态解析的xmlString 不能为空!!");
            return null;
        }
        try {
            return documentBuilder.parse(new InputSource(new StringReader(xmlString)));
        } catch (Exception e) {
            log.error("XML解析异常,请检查XML格式是否正确,errMsg:{}",e.getMessage());
        }
        return null;

    }
}


2. 测试


    public static void main(String[] args) {
        String sql1 = " <select id=\"dd\">\n" +
                "        select id,name from oss_file\n" +
                "        <where>\n" +
                "            <if test=\"url==1\">\n" +
                "                and id = 1\n" +
                "            </if>\n" +
                "            <if test=\"url!=1\">\n" +
                "                and name = #{people.name}\n" +
                "                and age = #{people.age}\n" +
                "            </if>\n" +
                "            <if test=\"url!=1\">\n" +
                "                and id in <foreach collection=\"idList\" index=\"index\" item=\"item\" open=\"(\" separator=\",\" close=\")\">\n" +
                "                #{item}\n" +
                "            </foreach>\n" +
                "            </if> group by id\n" +
                "        </where>\n" +
                "    </select>";

        Map<String, Object> map1 = new HashMap<String, Object>() {
            {
                put("url", 3);
                put("uop", "李四");
                put("idList", Arrays.asList(2, 3, 4, 5, 6));
                put("people", new HashMap<String, Object>() {
                    {
                        put("name", "刘备"); 
                        put("age", 22);
                    }
                })
            }
        };

        println(MyBatisUtil.formatSql(MyBatisUtil.parseDynamicXMLFormXmlStr(sql1, map1)))
        println("---------------------------------")

    }

测试结果:

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值