2023年6月21日发(作者:)

解析xml⽂件动态拼接sql语句现在有很多开源的框架,⽐如Mybatis、BeetlSQL、Hibernate、DbUtils。当我们需要⾃⼰⼿写sql的时候。使⽤Mybatis、BeetlSQL(这个个⼈更喜欢,因为结合了hibernate和mybatis各⾃的优点)框架相对来说更好,因为它将sql 放到配置⽂件⾥⾯。⽽不是硬编码到代码中。使⽤了这么多框架,如果想编程思想更上⼀层,不仅要怎么使⽤,还要学习其实现原理。接下来,⾃⼰来实现 解析xml⽂件 来 动态拼接 得到 sql。

1、问题引⼊?现在我想从下⾯的xml配置⽂件中,根据id得到正确的。⾥⾯的if、elseif、else这⼏个标签要可以实现条件判断的功能。该如何实现呢? 有点类似mybatis。⽂件名:, 如下:17181926 ]> select * from user where name = 'chenjiahao' and age >:age and age=:age order by create_date desc

2、实现思路:(1)不⽤条件判断的情况下:⽆需拼接,直接根据id获取到指定sql。(通过解析xml技术很容易实现)(2)有条件判断的情况下:⾸先根据id获取到指定的元素,再遍历⼦元素(递归思想,如果还有⼦元素则递归),通过逻辑判断,动态拼接成最终的sql。如果if elseif标签中属性test=" 表达式 "的结果为true就拼接,否则跳过。如何知道test="表达式"的值为真呢?可以借助第三⽅开源项⽬——表达式引擎来帮我们解决。3、使⽤到的技术dom4j、Fel表达式计算引擎。4、⾃⼰简单实现⽰例代码如下:dom4j 和 fel jar依赖:1112 4j dom4j 2.1.0 4j fel 0.8 829363738394647484950package ;

import ine;import ineImpl;import text;import text;import nt;import ntException;import t;import ;import der;

import ist;import p;import ;import ;import r;import n;

/** * 解析xml sql 动态拼接⽣成 * @author chen jia hao */public class XmlTest {

private static Map keywordMap = new HashMap<>();

private static Map sqlDocumentMap = new HashMap<>();

private static Map params = new HashMap<>();

private static FelEngine felEngine = new FelEngineImpl(); private static FelContext mapContext = new MapContext();

static{ ("if","if"); ("elseif","elseif"); ("else","else"); ("sql","sql"); ("sqls","sqls"); ("eq","=="); ("lt","<"); ("gt",">"); ("le","<="); ("ge",">="); }

public static void main(String[] args) { 56575859666768697677787986878889969798995

test();

}

public static void test(){ StringBuffer sqlBuffer = new StringBuffer(); try {

long startTime = tTimeMillis();

//设置参数 Map data = new HashMap<>(); ("age",18); setParams(data);

//解析sql Element sqlElement = getSqlElement("ser");

parseSql(sqlBuffer,sqlElement); String sqlText = ng().trim();

n("配置⽂件原sql:"+sqlText); n("预处理sql语句:"+getSql(sqlText));

List sqlParameterNames = getSqlParameterNames(sqlText);

boolean isFirst = true; ("参数:{"); for( item: et()){ if(isFirst){ isFirst = false; }else{ (","); } (()+":"+ue()); } n("}");

long endTime = tTimeMillis();

n("耗时:"+(endTime-startTime)+"ms");

} catch (Exception e) { tackTrace(); } }

/** * 根据name读取指定document⽂档,相对于classpath路径 * @author chen jia hao * @param name * @return */ public static Document getDocument(String name){ if((name)==null){ try { SAXReader saxReader = new SAXReader(); Document document = (temResourceAsStream(name+".xml")); (name,document); } catch (DocumentException e) { tackTrace(); } } return (name); }8648648648179180 }

/** * 根据sqlId获取指定sql元素 * @param sqlId * @return */ public static Element getSqlElement(String sqlId){ String[] sqlIdArr = ("."); String sqlFileName = sqlIdArr[0]; String sql = sqlIdArr[1]; return (Element)getDocument(sqlFileName).selectSingleNode("//sql[@id='"+sql+"']"); }

/** * 递归解析sql -- 核⼼部分 * @author chen jia hao * @param sqlBuffer * @param element */ public static void parseSql(StringBuffer sqlBuffer,Element element){ List tempList = t(); List nodeList = new ArrayList<>(); if( tempList!=null ){

//这⾥排除空,因为换⾏也会当被xml当成⼦元素 ().forEach(item->{ int length = t().replaceAll("s", "").length(); if(length>0){ (item); } });

//标记:if、elseif、else 其中是否有⼀个为真 boolean preIfIsTrue = false; for(int i=0,len=() ; i

Element childElem = (Element)node; String tagName = e(); if("if".equals(tagName) ){

String test = uteValue("test"); test = getEvalResult(test); //如果为真,递归解析 if("true".equals(test)){ parseSql(sqlBuffer,childElem); preIfIsTrue = true; }

}else if("elseif".equals(tagName)){

if(i>0){ Node preNode = (i-1); String preTagName = e(); if( !preIfIsTrue && ("if".equals(preTagName) || "elseif".equals(preTagName) ) ){ String currTest = uteValue("test"); currTest = getEvalResult(currTest); //如果之前对应的if或elseif test为false,且当前test为真,则才递归解析 if("true".equals(currTest)){ parseSql(sqlBuffer,childElem);75922224225226227228229234235236237238239244245 parseSql(sqlBuffer,childElem); preIfIsTrue = true; } } }

}else if("else".equals(tagName) ){

if(i>0){ Node preNode = (i-1); String preTagName = e(); if( "if".equals(preTagName) || "elseif".equals(preTagName) ){ //如果之前对应的if或elseif为false,则才递归解析 if(!preIfIsTrue){ parseSql(sqlBuffer,childElem); preIfIsTrue = false; } } } } } } } }

/** * ⽣成预处理sql * @author chen jia hao * @param sqlText * @return */ public static String getSql(String sqlText){

String reg = ":w+"; return eAll(reg, "?"); }

/** * 获取参数名列表 * @author chen jia hoa * @param sqlText * @return */ public static List getSqlParameterNames(String sqlText){ //获取参数 List paramNamesList = new ArrayList<>();

String reg = ":w+"; Pattern compile = e(reg); Matcher matcher = r(sqlText); while(()){ String group = ().substring(1); (group); } return paramNamesList; }

/** * 计算表达式的结果 * @author chen jia hao * @return "true" 或 "false" */ public static String getEvalResult(String exp){

if(exp!=null){246247248249254255256257258259264265266 exp = eAll(" eq "," == "). replaceAll(" lt "," < "). replaceAll(" le "," <= "). replaceAll(" gt "," > "). replaceAll(" ge "," >= "); }

Object result = (exp,mapContext); return ng(); }

/** * 设置参数 * @autor chen jia hao */ public static void setParams(Map data){ //参数 params = data; }

}打印结果:解析效率上还是不⾼,这⾥只是学习解析xml 动态拼接sql的⼀种思想。我们其实也可以换种思路去做。⽐如:结合模板引擎(velocity、)来动态⽣成sql。Beetlsql就是基于Beetl模板引擎来实现的,效率很⾼。

2023年6月21日发(作者:)

解析xml⽂件动态拼接sql语句现在有很多开源的框架,⽐如Mybatis、BeetlSQL、Hibernate、DbUtils。当我们需要⾃⼰⼿写sql的时候。使⽤Mybatis、BeetlSQL(这个个⼈更喜欢,因为结合了hibernate和mybatis各⾃的优点)框架相对来说更好,因为它将sql 放到配置⽂件⾥⾯。⽽不是硬编码到代码中。使⽤了这么多框架,如果想编程思想更上⼀层,不仅要怎么使⽤,还要学习其实现原理。接下来,⾃⼰来实现 解析xml⽂件 来 动态拼接 得到 sql。

1、问题引⼊?现在我想从下⾯的xml配置⽂件中,根据id得到正确的。⾥⾯的if、elseif、else这⼏个标签要可以实现条件判断的功能。该如何实现呢? 有点类似mybatis。⽂件名:, 如下:17181926 ]> select * from user where name = 'chenjiahao' and age >:age and age=:age order by create_date desc

2、实现思路:(1)不⽤条件判断的情况下:⽆需拼接,直接根据id获取到指定sql。(通过解析xml技术很容易实现)(2)有条件判断的情况下:⾸先根据id获取到指定的元素,再遍历⼦元素(递归思想,如果还有⼦元素则递归),通过逻辑判断,动态拼接成最终的sql。如果if elseif标签中属性test=" 表达式 "的结果为true就拼接,否则跳过。如何知道test="表达式"的值为真呢?可以借助第三⽅开源项⽬——表达式引擎来帮我们解决。3、使⽤到的技术dom4j、Fel表达式计算引擎。4、⾃⼰简单实现⽰例代码如下:dom4j 和 fel jar依赖:1112 4j dom4j 2.1.0 4j fel 0.8 829363738394647484950package ;

import ine;import ineImpl;import text;import text;import nt;import ntException;import t;import ;import der;

import ist;import p;import ;import ;import r;import n;

/** * 解析xml sql 动态拼接⽣成 * @author chen jia hao */public class XmlTest {

private static Map keywordMap = new HashMap<>();

private static Map sqlDocumentMap = new HashMap<>();

private static Map params = new HashMap<>();

private static FelEngine felEngine = new FelEngineImpl(); private static FelContext mapContext = new MapContext();

static{ ("if","if"); ("elseif","elseif"); ("else","else"); ("sql","sql"); ("sqls","sqls"); ("eq","=="); ("lt","<"); ("gt",">"); ("le","<="); ("ge",">="); }

public static void main(String[] args) { 56575859666768697677787986878889969798995

test();

}

public static void test(){ StringBuffer sqlBuffer = new StringBuffer(); try {

long startTime = tTimeMillis();

//设置参数 Map data = new HashMap<>(); ("age",18); setParams(data);

//解析sql Element sqlElement = getSqlElement("ser");

parseSql(sqlBuffer,sqlElement); String sqlText = ng().trim();

n("配置⽂件原sql:"+sqlText); n("预处理sql语句:"+getSql(sqlText));

List sqlParameterNames = getSqlParameterNames(sqlText);

boolean isFirst = true; ("参数:{"); for( item: et()){ if(isFirst){ isFirst = false; }else{ (","); } (()+":"+ue()); } n("}");

long endTime = tTimeMillis();

n("耗时:"+(endTime-startTime)+"ms");

} catch (Exception e) { tackTrace(); } }

/** * 根据name读取指定document⽂档,相对于classpath路径 * @author chen jia hao * @param name * @return */ public static Document getDocument(String name){ if((name)==null){ try { SAXReader saxReader = new SAXReader(); Document document = (temResourceAsStream(name+".xml")); (name,document); } catch (DocumentException e) { tackTrace(); } } return (name); }8648648648179180 }

/** * 根据sqlId获取指定sql元素 * @param sqlId * @return */ public static Element getSqlElement(String sqlId){ String[] sqlIdArr = ("."); String sqlFileName = sqlIdArr[0]; String sql = sqlIdArr[1]; return (Element)getDocument(sqlFileName).selectSingleNode("//sql[@id='"+sql+"']"); }

/** * 递归解析sql -- 核⼼部分 * @author chen jia hao * @param sqlBuffer * @param element */ public static void parseSql(StringBuffer sqlBuffer,Element element){ List tempList = t(); List nodeList = new ArrayList<>(); if( tempList!=null ){

//这⾥排除空,因为换⾏也会当被xml当成⼦元素 ().forEach(item->{ int length = t().replaceAll("s", "").length(); if(length>0){ (item); } });

//标记:if、elseif、else 其中是否有⼀个为真 boolean preIfIsTrue = false; for(int i=0,len=() ; i

Element childElem = (Element)node; String tagName = e(); if("if".equals(tagName) ){

String test = uteValue("test"); test = getEvalResult(test); //如果为真,递归解析 if("true".equals(test)){ parseSql(sqlBuffer,childElem); preIfIsTrue = true; }

}else if("elseif".equals(tagName)){

if(i>0){ Node preNode = (i-1); String preTagName = e(); if( !preIfIsTrue && ("if".equals(preTagName) || "elseif".equals(preTagName) ) ){ String currTest = uteValue("test"); currTest = getEvalResult(currTest); //如果之前对应的if或elseif test为false,且当前test为真,则才递归解析 if("true".equals(currTest)){ parseSql(sqlBuffer,childElem);75922224225226227228229234235236237238239244245 parseSql(sqlBuffer,childElem); preIfIsTrue = true; } } }

}else if("else".equals(tagName) ){

if(i>0){ Node preNode = (i-1); String preTagName = e(); if( "if".equals(preTagName) || "elseif".equals(preTagName) ){ //如果之前对应的if或elseif为false,则才递归解析 if(!preIfIsTrue){ parseSql(sqlBuffer,childElem); preIfIsTrue = false; } } } } } } } }

/** * ⽣成预处理sql * @author chen jia hao * @param sqlText * @return */ public static String getSql(String sqlText){

String reg = ":w+"; return eAll(reg, "?"); }

/** * 获取参数名列表 * @author chen jia hoa * @param sqlText * @return */ public static List getSqlParameterNames(String sqlText){ //获取参数 List paramNamesList = new ArrayList<>();

String reg = ":w+"; Pattern compile = e(reg); Matcher matcher = r(sqlText); while(()){ String group = ().substring(1); (group); } return paramNamesList; }

/** * 计算表达式的结果 * @author chen jia hao * @return "true" 或 "false" */ public static String getEvalResult(String exp){

if(exp!=null){246247248249254255256257258259264265266 exp = eAll(" eq "," == "). replaceAll(" lt "," < "). replaceAll(" le "," <= "). replaceAll(" gt "," > "). replaceAll(" ge "," >= "); }

Object result = (exp,mapContext); return ng(); }

/** * 设置参数 * @autor chen jia hao */ public static void setParams(Map data){ //参数 params = data; }

}打印结果:解析效率上还是不⾼,这⾥只是学习解析xml 动态拼接sql的⼀种思想。我们其实也可以换种思路去做。⽐如:结合模板引擎(velocity、)来动态⽣成sql。Beetlsql就是基于Beetl模板引擎来实现的,效率很⾼。