package org.jeecg.common.util.dynamic.db; import freemarker.cache.StringTemplateLoader; import freemarker.core.ParseException; import freemarker.core.TemplateClassResolver; import freemarker.template.Configuration; import freemarker.template.Template; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jeecg.common.constant.DataBaseConstant; import org.jeecg.common.constant.SymbolConstant; import org.jeecgframework.codegenerate.generate.util.SimpleFormat; import java.io.StringWriter; import java.util.Map; import java.util.regex.Pattern; /** * @author 赵俊夫 * @version V1.0 * @Title:FreemarkerHelper * @description:Freemarker引擎协助类 * @date Jul 5, 2013 2:58:29 PM */ @Slf4j public class FreemarkerParseFactory { private static final String ENCODE = "utf-8"; /** * 参数格式化工具类 */ private static final String MINI_DAO_FORMAT = "DaoFormat"; /** * 文件缓存 */ private static final Configuration TPL_CONFIG = new Configuration(); /** * SQL 缓存 */ private static final Configuration SQL_CONFIG = new Configuration(); private static StringTemplateLoader stringTemplateLoader = new StringTemplateLoader(); /**使用内嵌的(?ms)打开单行和多行模式*/ private final static Pattern NOTES_PATTERN = Pattern .compile("(?ms)/\\*.*?\\*/|^\\s*//.*?$"); static { TPL_CONFIG.setClassForTemplateLoading( new FreemarkerParseFactory().getClass(), "/"); TPL_CONFIG.setNumberFormat("0.#####################"); SQL_CONFIG.setTemplateLoader(stringTemplateLoader); SQL_CONFIG.setNumberFormat("0.#####################"); //classic_compatible设置,解决报空指针错误 SQL_CONFIG.setClassicCompatible(true); //update-begin-author:taoyan date:2022-8-10 for: freemarker模板注入问题 禁止解析ObjectConstructor,Execute和freemarker.template.utility.JythonRuntime。 //https://ackcent.com/in-depth-freemarker-template-injection/ SQL_CONFIG.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER); //update-end-author:taoyan date:2022-8-10 for: freemarker模板注入问题 禁止解析ObjectConstructor,Execute和freemarker.template.utility.JythonRuntime。 } /** * 判断模板是否存在 * * @throws Exception */ public static boolean isExistTemplate(String tplName) throws Exception { try { Template mytpl = TPL_CONFIG.getTemplate(tplName, "UTF-8"); if (mytpl == null) { return false; } } catch (Exception e) { //update-begin--Author:scott Date:20180320 for:解决问题 - 错误提示sql文件不存在,实际问题是sql freemarker用法错误----- if (e instanceof ParseException) { log.error(e.getMessage(), e.fillInStackTrace()); throw new Exception(e); } log.debug("----isExistTemplate----" + e.toString()); //update-end--Author:scott Date:20180320 for:解决问题 - 错误提示sql文件不存在,实际问题是sql freemarker用法错误------ return false; } return true; } /** * 解析ftl模板 * * @param tplName 模板名 * @param paras 参数 * @return */ public static String parseTemplate(String tplName, Map paras) { try { log.debug(" minidao sql templdate : " + tplName); StringWriter swriter = new StringWriter(); Template mytpl = TPL_CONFIG.getTemplate(tplName, ENCODE); if (paras.containsKey(MINI_DAO_FORMAT)) { throw new RuntimeException("DaoFormat 是 minidao 保留关键字,不允许使用 ,请更改参数定义!"); } paras.put(MINI_DAO_FORMAT, new SimpleFormat()); mytpl.process(paras, swriter); String sql = getSqlText(swriter.toString()); paras.remove(MINI_DAO_FORMAT); return sql; } catch (Exception e) { log.error(e.getMessage(), e.fillInStackTrace()); log.error("发送一次的模板key:{ " + tplName + " }"); //System.err.println(e.getMessage()); //System.err.println("模板名:{ "+ tplName +" }"); throw new RuntimeException("解析SQL模板异常"); } } /** * 解析ftl * * @param tplContent 模板内容 * @param paras 参数 * @return String 模板解析后内容 */ public static String parseTemplateContent(String tplContent,Map paras) { return parseTemplateContent(tplContent, paras, false); } public static String parseTemplateContent(String tplContent, Map paras, boolean keepSpace) { try { String sqlUnderline="sql_"; StringWriter swriter = new StringWriter(); if (stringTemplateLoader.findTemplateSource(sqlUnderline + tplContent.hashCode()) == null) { stringTemplateLoader.putTemplate(sqlUnderline + tplContent.hashCode(), tplContent); } Template mytpl = SQL_CONFIG.getTemplate(sqlUnderline + tplContent.hashCode(), ENCODE); if (paras.containsKey(MINI_DAO_FORMAT)) { throw new RuntimeException("DaoFormat 是 minidao 保留关键字,不允许使用 ,请更改参数定义!"); } paras.put(MINI_DAO_FORMAT, new SimpleFormat()); mytpl.process(paras, swriter); String sql = getSqlText(swriter.toString(), keepSpace); paras.remove(MINI_DAO_FORMAT); return sql; } catch (Exception e) { log.error(e.getMessage(), e.fillInStackTrace()); log.error("发送一次的模板key:{ " + tplContent + " }"); //System.err.println(e.getMessage()); //System.err.println("模板内容:{ "+ tplContent +" }"); throw new RuntimeException("解析SQL模板异常"); } } /** * 除去无效字段,去掉注释 不然批量处理可能报错 去除无效的等于 */ private static String getSqlText(String sql) { return getSqlText(sql, false); } private static String getSqlText(String sql, boolean keepSpace) { // 将注释替换成"" sql = NOTES_PATTERN.matcher(sql).replaceAll(""); if (!keepSpace) { sql = sql.replaceAll("\\n", " ").replaceAll("\\t", " ") .replaceAll("\\s{1,}", " ").trim(); } // 去掉 最后是 where这样的问题 //where空格 "where " String whereSpace = DataBaseConstant.SQL_WHERE+" "; //"where and" String whereAnd = DataBaseConstant.SQL_WHERE+" and"; //", where" String commaWhere = SymbolConstant.COMMA+" "+DataBaseConstant.SQL_WHERE; //", " String commaSpace = SymbolConstant.COMMA + " "; if (sql.endsWith(DataBaseConstant.SQL_WHERE) || sql.endsWith(whereSpace)) { sql = sql.substring(0, sql.lastIndexOf("where")); } // 去掉where and 这样的问题 int index = 0; while ((index = StringUtils.indexOfIgnoreCase(sql, whereAnd, index)) != -1) { sql = sql.substring(0, index + 5) + sql.substring(index + 9, sql.length()); } // 去掉 , where 这样的问题 index = 0; while ((index = StringUtils.indexOfIgnoreCase(sql, commaWhere, index)) != -1) { sql = sql.substring(0, index) + sql.substring(index + 1, sql.length()); } // 去掉 最后是 ,这样的问题 if (sql.endsWith(SymbolConstant.COMMA) || sql.endsWith(commaSpace)) { sql = sql.substring(0, sql.lastIndexOf(",")); } return sql; } }