解析xml⽂件动态拼接sql语句
现在有很多开源的框架,⽐如Mybatis、BeetlSQL、Hibernate、DbUtils。当我们需要⾃⼰⼿写sql的时候。使⽤Mybatis、
BeetlSQL(这个个⼈更喜欢,因为结合了hibernate和mybatis各⾃的优点)框架相对来说更好,因为它将sql 放到配置⽂件⾥⾯。⽽不是硬编码到代码中。使⽤了这么多框架,如果想编程思想更上⼀层,不仅要怎么使⽤,还要学习其实现原理。接下来,⾃⼰来实现 解析xml⽂件 来 动态拼接 得到 sql。
1、问题引⼊?
现在我想从下⾯的xml配置⽂件中,根据id得到正确的。⾥⾯的if、elseif、else这⼏个标签要可以实现条件判断的功能。该如何实现呢? 有点类似mybatis。
⽂件名:l, 如下:
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE sqls[
3 <!ELEMENT sqls ( sql* ) >
4 <!ELEMENT sql ( #PCDATA | if | else | elseif )*>
5 <!ATTLIST sql id ID #REQUIRED>
6 <!ELEMENT if ( #PCDATA | if | else | elseif )* >
7 <!ATTLIST if test CDATA #REQUIRED>
8 <!ELEMENT elseif ( #PCDATA | if | else | elseif )* >
9 <!ATTLIST elseif test CDATA #REQUIRED>
10 <!ELEMENT else ( #PCDATA | if | else | elseif )* >使用dom4j解析xml文件
11 ]>
12<sqls>
13 <sql id="queryUser">
14 select * from user where name = 'chenjiahao'
15 <if test="age gt 18">
16 and age >:age
17 </if>
18 <elseif test="age lt 18">
19 <![CDATA[ and age <:age ]]>
20 </elseif>
21 <else>
22 and age=:age
23 </else>
24 order by create_date desc
25 </sql>
26</sqls>
2、实现思路:
(1)不⽤条件判断的情况下:
⽆需拼接,直接根据id获取到指定sql。(通过解析xml技术很容易实现)
(2)有条件判断的情况下:
⾸先根据id获取到指定的元素,再遍历⼦元素(递归思想,如果还有⼦元素则递归),通过逻辑判断,动态拼接成最终的sql。如果if elseif 标签中属性test=" 表达式 "的结果为true就拼接,否则跳过。
如何知道test="表达式"的值为真呢?可以借助第三⽅开源项⽬——表达式引擎来帮我们解决。
3、使⽤到的技术
dom4j、Fel表达式计算引擎。
4、⾃⼰简单实现⽰例代码如下:
1 <!-- mvnrepository/artifact/org.dom4j/dom4j -->
2 <dependency>
3 <groupId>org.dom4j</groupId>
4 <artifactId>dom4j</artifactId>
5 <version>2.1.0</version>
6 </dependency>
7 <!--fel 表达式引擎-->
8 <dependency>
9 <groupId>org.eweb4j</groupId>
10 <artifactId>fel</artifactId>
11 <version>0.8</version>
12 </dependency>
1package st.xml;
2
pineyu.fel.FelEngine;
pineyu.fel.FelEngineImpl;
t.FelContext;
t.MapContext;
7import org.dom4j.Document;
8import org.dom4j.DocumentException;
9import org.dom4j.Element;
10import org.dom4j.Node;
11import org.dom4j.io.SAXReader;
12
13import java.util.ArrayList;
14import java.util.HashMap;
15import java.util.List;
16import java.util.Map;
17import Matcher;
18import Pattern;
19
20/**
21 * 解析xml sql 动态拼接⽣成
22 * @author chen jia hao
23 */
24public class XmlTest {
25
26 private static Map<String, String> keywordMap = new HashMap<>();
27
28 private static Map<String,Document> sqlDocumentMap = new HashMap<>(); 29
30 private static Map<String, Object> params = new HashMap<>();
31
32 private static FelEngine felEngine = new FelEngineImpl();
33 private static FelContext mapContext = new MapContext();
34
35 static{
36 keywordMap.put("if","if");
37 keywordMap.put("elseif","elseif");
38 keywordMap.put("else","else");
39 keywordMap.put("sql","sql");
40 keywordMap.put("sqls","sqls");
41 keywordMap.put("eq","==");
42 keywordMap.put("lt","<");
43 keywordMap.put("gt",">");
44 keywordMap.put("le","<=");
45 keywordMap.put("ge",">=");
46 }
47
48
49 public static void main(String[] args) {
51 test();
52
53 }
54
55 public static void test(){
56 StringBuffer sqlBuffer = new StringBuffer();
57 try {
58
59 long startTime = System.currentTimeMillis();
60
61 //设置参数
62 Map<String, Object> data = new HashMap<>();
63 data.put("age",18);
64 setParams(data);
65
66 //解析sql
67 Element sqlElement = getSqlElement("test3.queryUser");
68
69 parseSql(sqlBuffer,sqlElement);
70 String sqlText = String().trim();
71
72 System.out.println("配置⽂件原sql:"+sqlText);
73 System.out.println("预处理sql语句:"+getSql(sqlText));
74
75 List<String> sqlParameterNames = getSqlParameterNames(sqlText);
76
77 boolean isFirst = true;
78 System.out.print("参数:{");
79 for(Map.Entry<String,Object> item: Set()){
80 if(isFirst){
81 isFirst = false;
82 }else{
83 System.out.print(",");
84 }
85 System.out.Key()+":"+Value());
86 }
87 System.out.println("}");
88
89 long endTime = System.currentTimeMillis();
90
91 System.out.println("耗时:"+(endTime-startTime)+"ms");
92
93 } catch (Exception e) {
94 e.printStackTrace();
95 }
96 }
97
98 /**
99 * 根据name读取指定document⽂档,相对于classpath路径
100 * @author chen jia hao
101 * @param name
102 * @return
103 */
104 public static Document getDocument(String name){
105 (name)==null){
106 try {
107 SAXReader saxReader = new SAXReader();
108 Document document = SystemResourceAsStream(name+".xml")); 109 sqlDocumentMap.put(name,document);
110 } catch (DocumentException e) {
111 e.printStackTrace();
112 }
113 }
114 (name);
116
117 /**
118 * 根据sqlId获取指定sql元素
119 * @param sqlId
120 * @return
121 */
122 public static Element getSqlElement(String sqlId){
123 String[] sqlIdArr = sqlId.split("\\.");
124 String sqlFileName = sqlIdArr[0];
125 String sql = sqlIdArr[1];
126 return (Element)getDocument(sqlFileName).selectSingleNode("//sql[@id='"+sql+"']");
127 }
128
129 /**
130 * 递归解析sql -- 核⼼部分
131 * @author chen jia hao
132 * @param sqlBuffer
133 * @param element
134 */
135 public static void parseSql(StringBuffer sqlBuffer,Element element){
136 List<Node> tempList = t();
137 List<Node> nodeList = new ArrayList<>();
138 if( tempList!=null ){
139
140 //这⾥排除空,因为换⾏也会当被xml当成⼦元素
141 tempList.stream().forEach(item->{
142 int length = Text().replaceAll("\\s", "").length();
143 if(length>0){
144 nodeList.add(item);
145 }
146 });
147
148 //标记:if、elseif、else 其中是否有⼀个为真
149 boolean preIfIsTrue = false;
150 for(int i=0,len=nodeList.size() ; i<len ;i++){
151 Node node = (i);
152 //⽂本类型或CDATA区,直接获取sql
153 if(Node.TEXT_NODE == NodeType() || Node.CDATA_SECTION_NODE == NodeType()){ 154 sqlBuffer.Text().replaceAll("\\s+"," "));
155 preIfIsTrue = false;
156 }else if(Node.ELEMENT_NODE == NodeType()){
157
158 Element childElem = (Element)node;
159 String tagName = Name();
160 if("if".equals(tagName) ){
161
162 String test = childElem.attributeValue("test");
163 test = getEvalResult(test);
164 //如果为真,递归解析
165 if("true".equals(test)){
166 parseSql(sqlBuffer,childElem);
167 preIfIsTrue = true;
168 }
169
170 }else if("elseif".equals(tagName)){
171
172 if(i>0){
173 Node preNode = (i-1);
174 String preTagName = Name();
175 if( !preIfIsTrue && ("if".equals(preTagName) || "elseif".equals(preTagName) ) ){
176 String currTest = childElem.attributeValue("test");
177 currTest = getEvalResult(currTest);
178 //如果之前对应的if或elseif test为false,且当前test为真,则才递归解析
179 if("true".equals(currTest)){
180 parseSql(sqlBuffer,childElem);
181 preIfIsTrue = true;
182 }
183 }
184 }
185
186 }else if("else".equals(tagName) ){
187
188 if(i>0){
189 Node preNode = (i-1);
190 String preTagName = Name();
191 if( "if".equals(preTagName) || "elseif".equals(preTagName) ){ 192 //如果之前对应的if或elseif为false,则才递归解析
193 if(!preIfIsTrue){
194 parseSql(sqlBuffer,childElem);
195 preIfIsTrue = false;
196 }
197 }
198 }
199 }
200 }
201 }
202 }
203 }
204
205 /**
206 * ⽣成预处理sql
207 * @author chen jia hao
208 * @param sqlText
209 * @return
210 */
211 public static String getSql(String sqlText){
212
213 String reg = ":\\w+";
214 placeAll(reg, "?");
215 }
216
217 /**
218 * 获取参数名列表
219 * @author chen jia hoa
220 * @param sqlText
221 * @return
222 */
223 public static List<String> getSqlParameterNames(String sqlText){
224 //获取参数
225 List<String> paramNamesList = new ArrayList<>();
226
227 String reg = ":\\w+";
228 Pattern compile = Patternpile(reg);
229 Matcher matcher = compile.matcher(sqlText);
230 while(matcher.find()){
231 String group = up().substring(1);
232 paramNamesList.add(group);
233 }
234 return paramNamesList;
235 }
236
237
238 /**
239 * 计算表达式的结果
240 * @author chen jia hao
241 * @return "true" 或 "false"
242 */
243 public static String getEvalResult(String exp){
244
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论