有一个带有mybatis语法的sql语句,如下:
select * from cfg_export_template where id = #{id} <if test='id != null'> AND 1 = 1 </if>
需求是将sql和占位符相关的数据丢给mybatis,获取到最终可执行的SQL。
解决思路:
1、像if、where等动态标签跟#{}这种占位符不一样,动态标签是mybatis是通过xml的结构去解析的,因为我们拿到手的是字符串,如果直接丢给mybats是无法将这些动态标签进行解析的。所以需要构建成xml格式去处理。参考方法getExecutableSql
2、对于#{}这类的占位符在Mybatis预编译语句后会被?所替换,可以使用ParameterHandler去获取到最终的可执行SQL。参考方法replaceParameters
public class MyBatisDynamicSqlExecutor {private SqlSessionFactory sqlSessionFactory;public MyBatisDynamicSqlExecutor(SqlSessionFactory sqlSessionFactory) {this.sqlSessionFactory = sqlSessionFactory;}/*** 执行传递的SQL 预编译后,返回最终可执行的SQL* @param dynamicSql* @param params* @return*/public String getExecutableSql(String dynamicSql, Map<String, Object> params) throws SQLException {//对字符串中的符号转义dynamicSql = StringEscapeUtils.escapeXml10(dynamicSql);params.put("sql", dynamicSql);Configuration configuration = sqlSessionFactory.getConfiguration();String xml = "<select>" + dynamicSql + "</select>";XPathParser parser = new XPathParser(xml);List<XNode> xNodes = parser.evalNodes("select|insert|update|delete");BoundSql boundSql = null;if (xNodes != null && xNodes.size() != 0) {if (xNodes.size() > 1) {throw new RuntimeException("只能解析一句sql");} else {LanguageDriver langDriver = configuration.getLanguageDriver((Class)null);XNode node = (XNode)xNodes.get(0);SqlSource sqlSource = langDriver.createSqlSource(configuration, node, (Class)null);boundSql = sqlSource.getBoundSql(params);}} else {throw new RuntimeException("找不到sql语句");}// 获取SQL并替换参数return replaceParameters(boundSql, params,boundSql.getSql().substring(0,boundSql.getSql().indexOf(" ")));}/*** 使用ParameterHandler替换参数* @param boundSql* @param params* @param queryMethod 查询方式 select/delete...* @return* @throws SQLException*/private String replaceParameters(BoundSql boundSql, Map<String, Object> params,String queryMethod) throws SQLException {// 获取配置和MappedStatementConfiguration configuration = sqlSessionFactory.getConfiguration();MappedStatement mappedStatement = configuration.getMappedStatement("cn.com.fssg.platform.modules.engine.mapper.DynamicMapper.executeDynamicSql");// 创建ParameterHandlerParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, params, boundSql);Connection connection = configuration.getEnvironment().getDataSource().getConnection();PreparedStatement ps = connection.prepareStatement(boundSql.getSql());parameterHandler.setParameters(ps);// 获取PreparedStatement的最终SQL,这里PreparedStatement已经注入了参数String preparedSql = ps.toString();// 去掉PreparedStatement前缀,保留实际的SQL部分,假设SQL是从select开始int sqlIndex = preparedSql.indexOf(queryMethod);return preparedSql.substring(sqlIndex);}}