解析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小时内删除。