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("---------------------------------")
}
测试结果:
![](https://img-blog.csdnimg.cn/20210821224529168.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzODczMDEz,size_16,color_FFFFFF,t_70)