发现要坚持写博客真的是一件很困难的事情,各种原因都会导致顾不上博客。本来打算写自己动手实现orm,看看时间,还是先实现一个动态sql,下次有时间再补上orm完整的实现吧。

  用过mybatis的人,估计对动态sql都不陌生,如果没有用过,就当看看热闹吧。我第一次接触mysql是在大四的时候,当时就觉得动态sql这东西很牛,很灵活,一直想搞明白怎么实现的,尽管当时已经能够写ioc,mvc和简单的orm框架(仿mybaits但是没有动态sql部分),但是仍然找不到mybatis核心的动态sql到底在哪实现的,怎么实现的,可能是那些代码太绕根本没法看懂,直到目前,我都没有勇气去看mybatis的动态sql部分,大概是天生对算法有莫名其妙的敬畏吧。

  几年前因为想做一个配置平台,想用解析型语言替代java的实现,可以让配置人员在页面上方便的编写少量代码实现复杂的业务逻辑(包括数据库操作)。当时java已经有js解析引擎,但是大多数人都说效率太低,不知道我发什么疯就想到自己实现一个解析语言。不过实现自己的语言也是我一直的梦想,解析语言相对编译型语言入手简单,于是我就果断动手了,写完才知道,其实自己的实现估计还没有当时的js引擎效率高,那时的我真的是很年轻很简单。今天谈到的动态sql实现其实就是受到那时候解析语言的启发。

  废话不多说直接开始聊动态sql,请看下面例子,先声明这里的例子并不是一个正确的sql的写法,只是想写一个尽量复杂的嵌套结构,如果把这种复杂的情况实现了,那么简单一点的就更加不在话下了。

  1. delete from pl_pagewidget
  2. <if test="widgetcodes != null">
  3. where pagewidgetcode in
  4. <foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
  5. <if test="index == 0">
  6. #{item}
  7. </if>
  8. <foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
  9. #{b}
  10. </foreach>
  11. </foreach>
  12. </if>
  13. <if test="a != null">
  14. and a = #{a}
  15. </if>

  要实现解析出上面例子的sql,首先一个难点类似是test属性里的条件怎么判断真假,不过这个难点在struts2中学到的ognl表达式面前就比较小儿科了。不知道有么有朋友遇到过一个比较奇葩的现象,就是有时候明明在mybatis动态sql中写如下表达式,但是当n=0的时候居然是满足条件的也就是test里的值是false,0居然不能满足这个表达式的条件,这里就是ognl库的原因了。没办法它就是这么玩的,当成特殊情况记住就可以了

  1. test="n != null and n !=''"

  ognl表达式使用很方便如下

  1. import java.util.HashMap;
  2. import java.util.Map;
  3. import ognl.Ognl;
  4. public class OgnlTest {
  5. //输出结果:false
  6. public static void main(String[] args) throws Exception {
  7. String con1 = "n != null and n != ''";
  8. Map<String,Object> root = new HashMap<>();
  9. root.put("n", 0);
  10. System.out.println(Ognl.getValue(con1,root));
  11. }
  12. }

  要实现解析上面例子的sql,第二个难点就是虽然这个sql披上一层xml的皮就是一个标准的sql,如下

  1. <sql>
  2. delete from pl_pagewidget
  3. <if test="widgetcodes != null">
  4. where pagewidgetcode in
  5. <foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
  6. <if test="index == 0">
  7. #{item}
  8. </if>
  9. <foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
  10. #{b}
  11. </foreach>
  12. </foreach>
  13. </if>
  14. <if test="a != null">
  15. and a = #{a}
  16. </if>
  17. </sql>

  但是要解析上面的xml和我们平时不一样,这个xml是标签和文本混合的,正常我们开发中应该很少会用到解析这种xml。不过我们常用的解析xml的工具dom4j其实可以很好的解析这种sql,只不过很少可能用到。Element类的content()方法就可以返回一个Node的集合,再通过遍历这个集合,判断每个Node的类型就可以了。解决了这两个重点,只需要加上一点技巧就可以解析这种动态sql了。

  我用到的技巧是根据java语法格式得到的启发。比如java中有局部变量和全局变量,不考虑引用传递这种情况,如果全局变量int i = 1;方法里面传入这个全局变量,然后在方法里面修改,在方法里面看到的是改变后的值,但是在方法外面看到的仍然是1。这个现象其实学过java应该都知道。还有就是当方法调用的时候,方法里面可以看到全局变量,也可以看到局部变量,方法调用结束后局部变量会被清空释放(看垃圾搜集器高兴)。介绍了这些直接上代码了

  1. import java.io.StringReader;
  2. import java.text.SimpleDateFormat;
  3. import java.util.Arrays;
  4. import java.util.Date;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.regex.Matcher;
  9. import java.util.regex.Pattern;
  10. import org.apache.commons.collections.MapUtils;
  11. import org.apache.commons.lang.StringUtils;
  12. import org.dom4j.Document;
  13. import org.dom4j.Element;
  14. import org.dom4j.Node;
  15. import org.dom4j.Text;
  16. import org.dom4j.io.SAXReader;
  17. import com.rd.sql.Attrs;
  18. import com.rd.sql.BaseNode;
  19. import com.rd.sql.NodeFactory;
  20. public class SqlParser {
  21. private Map<String,Object> currParams = new HashMap<String,Object>();
  22. /**
  23. delete from pl_pagewidget
  24. <if test="widgetcodes != null">
  25. where pagewidgetcode in
  26. <foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
  27. <if test="index == 0">
  28. #{item}
  29. </if>
  30. <foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
  31. #{b}
  32. </foreach>
  33. </foreach>
  34. </if>
  35. <if test="a != null">
  36. and a = #{a}
  37. </if>
  38. */
  39. public static void main(String[] args) throws Exception {
  40. Map<String, Object> map = new HashMap<String, Object>();
  41. map.put("widgetcodes", Arrays.asList("1", "2"));
  42. map.put("bs", Arrays.asList("3", "4"));
  43. map.put("a", 1);
  44. SqlParser parser = new SqlParser();
  45. System.out
  46. .println(parser.parser("delete from pl_pagewidget\n"
  47. + "\t<if test=\"widgetcodes != null\">\n"
  48. + "\t\twhere pagewidgetcode in\n"
  49. + "\t\t<foreach collection=\"widgetcodes\" item=\"item\" index=\"index\" open=\"(\" separator=\",\" close=\")\">\n"
  50. + "\t\t <if test=\"index == 0\">\n"
  51. + "\t\t #{item}\n"
  52. + "\t\t </if>\n"
  53. + "\t\t <foreach collection=\"bs\" item=\"b\" index=\"index1\" open=\"(\" separator=\",\" close=\")\">\n"
  54. + "\t\t\t#{b}\n" + "\t\t </foreach>\n"
  55. + "\t\t</foreach>\n" + "\t</if>\n"
  56. + "\t<if test=\"a != null\">\n"
  57. + "\t\tand a = #{a}\n" + "\t</if>\n", map));
  58. System.out.println(parser.getParams());
  59. }
  60. public String parser(String xml, Map<String, Object> params)
  61. throws Exception {
  62. // xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+xml;
  63. //给输入的动态sql套一层xml标签
  64. xml = "<sql>"+xml+"</sql>";
  65. SAXReader reader = new SAXReader(false);
  66. Document document = reader.read(new StringReader(xml));
  67. Element element = document.getRootElement();
  68. Map<String, Object> currParams = new HashMap<String, Object>();
  69. StringBuilder sb = new StringBuilder();
  70. //开始解析
  71. parserElement(element, currParams, params, sb);
  72. return sb.toString();
  73. }
  74. /**
  75. * 使用递归解析动态sql
  76. * @param ele1 待解析的xml标签
  77. * @param currParams
  78. * @param globalParams
  79. * @param sb
  80. * @throws Exception
  81. */
  82. private void parserElement(Element ele1, Map<String, Object> currParams,
  83. Map<String, Object> globalParams, StringBuilder sb)
  84. throws Exception {
  85. // 解析一个节点,比如解析到了一个if节点,假如test判断为true这里就返回true
  86. TempVal val = parserOneElement(currParams, globalParams, ele1, sb);
  87. //得到解析的这个节点的抽象节点对象
  88. BaseNode node = val.getNode();
  89. /**
  90. * 实际上这句之上的语句只是解析了xml的标签,并没有解析标签里的内容,这里
  91. * 表示要解析内容之前,如果有前置操作做一点前置操作
  92. */
  93. node.pre(currParams, globalParams, ele1, sb);
  94. //判断是否还需要解析节点里的内容,例如if节点test结果为true
  95. boolean flag = val.isContinue();
  96. // 得到该节点下的所有子节点的集合,包含普通文本
  97. List<Node> nodes = ele1.content();
  98. if (flag && !nodes.isEmpty()) {
  99. /**
  100. * 这里表示要进一步解析节点里的内容了,可以把节点类比成一个方法的外壳
  101. * 里面的内容类比成方法里的具体语句,开始解析节点的内容之前
  102. * 先创建本节点下的局部参数的容器,最方便当然是map
  103. */
  104. Map<String, Object> params = new HashMap<String, Object>();
  105. /**
  106. * 把外面传进来的局部参数,直接放入容器,由于本例中参数都是常用数据类型
  107. * 不会存在引用类型所以,这里相当于是一个copy,为了不影响外面传入的对象
  108. * 可以类比方法调用传入参数的情况
  109. */
  110. params.putAll(currParams);
  111. //循环所有子节点
  112. for (int i = 0; i < nodes.size();) {
  113. Node n = nodes.get(i);
  114. //如果节点是普通文本
  115. if (n instanceof Text) {
  116. String text = ((Text) n).getStringValue();
  117. if (StringUtils.isNotEmpty(text.trim())) {
  118. //处理一下文本,如处理#{xx},直接替换${yy}为真实传入的值
  119. sb.append(handText(text, params,globalParams));
  120. }
  121. i++;
  122. } else if (n instanceof Element) {
  123. Element e1 = (Element) n;
  124. // 递归解析xml子元素
  125. parserElement(e1, params, globalParams, sb);
  126. // 如果循环标志不为true则解析下一个标签
  127. // 这里表示需要重复解析这个循环标签,则i不变,反之继续处理下一个元素
  128. boolean while_flag = MapUtils.getBoolean(params,
  129. Attrs.WHILE_FLAG, false);
  130. if (!while_flag
  131. || !NodeFactory.isWhile(n.getName())
  132. || e1.attributeValue(Attrs.INDEX) == null
  133. || !e1.attributeValue(Attrs.INDEX).equals(
  134. params.get(Attrs.WHILE_INDEX))) {
  135. i++;
  136. }
  137. }
  138. }
  139. //节点处理之后做一些啥事
  140. node.after(currParams, globalParams, ele1, sb);
  141. // 回收当前作用域参数
  142. params.clear();
  143. params = null;
  144. }
  145. }
  146. /**
  147. * 处理文本替换掉#{item}这种参数
  148. * @param str
  149. * @param params
  150. * @return
  151. * @throws Exception
  152. */
  153. private String handText(String str, Map<String, Object> params,Map<String, Object> globalParams)
  154. throws Exception {
  155. //获取foreach这种标签中用于记录循环的变量
  156. String indexStr = MapUtils.getString(params, Attrs.WHILE_INDEX);
  157. Integer index = null;
  158. if(StringUtils.isNotEmpty(indexStr)) {
  159. index = MapUtils.getInteger(params, indexStr);
  160. }
  161. //匹配#{a}这种参数
  162. String reg1 = "(#\\{)(\\w+)(\\})";
  163. //匹配${a}这种参数
  164. String reg2 = "(\\$\\{)(\\w+)(\\})";
  165. Pattern p1 = Pattern.compile(reg1);
  166. Matcher m1 = p1.matcher(str);
  167. Pattern p2 = Pattern.compile(reg2);
  168. Matcher m2 = p2.matcher(str);
  169. String whileList = MapUtils.getString(params, Attrs.WHILE_LIST);
  170. Map<String,Object> allParams = getAllParams(params, globalParams);
  171. while(m1.find()) {
  172. String tmpKey = m1.group(2);
  173. String key = whileList == null?tmpKey:(whileList+"_"+tmpKey);
  174. key = index == null?key:(key+index);
  175. String reKey = "#{"+key+"}";
  176. //如果在foreach类似的循环里,可能需要将参数#{xx}替换成#{xx_0},#{xx_1}
  177. str = str.replace(m1.group(0), reKey);
  178. currParams.put(key, allParams.get(tmpKey));
  179. }
  180. while(m2.find()) {
  181. String tmpKey = m2.group(2);
  182. Object value = allParams.get(tmpKey);
  183. if(value != null) {
  184. str = str.replace(m2.group(0), getValue(value));
  185. }
  186. }
  187. return str;
  188. }
  189. private String getValue(Object value) {
  190. String result = "";
  191. if(value instanceof Date) {
  192. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  193. result = sdf.format((Date)value);
  194. } else {
  195. result = String.valueOf(value);
  196. }
  197. return result;
  198. }
  199. private Map<String, Object> getAllParams(Map<String, Object> currParams,
  200. Map<String, Object> globalParams) {
  201. Map<String,Object> allParams = new HashMap<String,Object>();
  202. allParams.putAll(globalParams);
  203. allParams.putAll(currParams);
  204. return allParams;
  205. }
  206. // 解析一个xml元素
  207. private TempVal parserOneElement(Map<String, Object> currParams,
  208. Map<String, Object> globalParams, Element ele, StringBuilder sb)
  209. throws Exception {
  210. //获取xml标签名
  211. String eleName = ele.getName();
  212. //解析一个节点后是否继续,如遇到if这种节点,就需要判断test里是否为空
  213. boolean isContinue = false;
  214. //声明一个抽象节点
  215. BaseNode node = null;
  216. if (StringUtils.isNotEmpty(eleName)) {
  217. //使用节点工厂根据节点名得到一个节点对象比如是if节点还是foreach节点
  218. node = NodeFactory.create(eleName);
  219. //解析一下这个节点,返回是否还需要解析节点里的内容
  220. isContinue = node.parse(currParams, globalParams, ele, sb);
  221. }
  222. return new TempVal(isContinue, ele, node);
  223. }
  224. public Map<String, Object> getParams() {
  225. return currParams;
  226. }
  227. /**
  228. * 封装一个xml元素被解析后的结果
  229. * @author rongdi
  230. */
  231. final static class TempVal {
  232. private boolean isContinue;
  233. private Element ele;
  234. private BaseNode node;
  235. public TempVal(boolean isContinue, Element ele, BaseNode node) {
  236. this.isContinue = isContinue;
  237. this.ele = ele;
  238. this.node = node;
  239. }
  240. public boolean isContinue() {
  241. return isContinue;
  242. }
  243. public void setContinue(boolean isContinue) {
  244. this.isContinue = isContinue;
  245. }
  246. public Element getEle() {
  247. return ele;
  248. }
  249. public void setEle(Element ele) {
  250. this.ele = ele;
  251. }
  252. public BaseNode getNode() {
  253. return node;
  254. }
  255. public void setNode(BaseNode node) {
  256. this.node = node;
  257. }
  258. }
  259. }
  1. import org.dom4j.Element;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4.  
  5. /**
  6. * 抽象节点
  7. * @author rongdi
  8. */
  9. public abstract class BaseNode {
  10. public abstract boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele,StringBuilder sb) throws Exception;
  11. public void pre(Map<String, Object> currParams,Map<String, Object> globalParams,Element ele,StringBuilder sb) throws Exception {
  12. }
  13. public void after(Map<String, Object> currParams,Map<String, Object> globalParams,Element ele,StringBuilder sb) throws Exception {
  14. }
  15. protected Map<String, Object> getAllParams(Map<String, Object> currParams,
  16. Map<String, Object> globalParams) {
  17. Map<String,Object> allParams = new HashMap<String,Object>();
  18. allParams.putAll(globalParams);
  19. allParams.putAll(currParams);
  20. return allParams;
  21. }
  22. }
  1. import java.util.Map;
  2. import ognl.Ognl;
  3. import org.apache.commons.lang.StringUtils;
  4. import org.dom4j.Element;
  5.  
  6. /**
  7. * if节点
  8. * @author rongdi
  9. */
  10. public class IfNode extends BaseNode{
  11.  
  12. @Override
  13. public boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele,StringBuilder sb) throws Exception {
  14. //得到if节点的test属性
  15. String testStr = ele.attributeValue("test");
  16. boolean test = false;
  17. try {
  18. if(StringUtils.isNotEmpty(testStr)) {
  19. //合并全局变量和局部变量
  20. Map<String, Object> allParams = getAllParams(currParams,globalParams);
  21. //使用ognl判断true或者false
  22. test = (Boolean) Ognl.getValue(testStr,allParams);
  23. }
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. throw new Exception("判断操作参数"+testStr+"不合法");
  27. }
  28.  
  29. if(ele.content() != null && ele.content().size()==0) {
  30. test = true;
  31. }
  32.  
  33. return test;
  34. }
  35.  
  36. }
  1. import java.util.ArrayList;
  2. import java.util.HashMap;
  3. import java.util.List;
  4. import java.util.Map;
  5. import java.util.Set;
  6. import ognl.Ognl;
  7. import org.apache.commons.collections.MapUtils;
  8. import org.apache.commons.lang.StringUtils;
  9. import org.dom4j.Element;
  10.  
  11. /**
  12. foreach节点属性如下
  13. collection 需要遍历的集合
  14. item 遍历集合后每个元素存放的变量
  15. index 遍历集合的索引数如0,1,2...
  16. separator 遍历后以指定分隔符拼接
  17. open 遍历后拼接开始的符号如 (
  18. close 遍历后拼接结束的符号如 )
  19. */
  20. public class ForeachNode extends BaseNode {
  21.  
  22. @Override
  23. public boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele, StringBuilder sb) throws Exception {
  24.  
  25. String conditionStr = null;
  26. String collectionStr = ele.attributeValue("collection");
  27. String itemStr = ele.attributeValue("item");
  28. String index = ele.attributeValue("index");
  29. String separatorStr = ele.attributeValue("separator");
  30. String openStr = ele.attributeValue("open");
  31. String closeStr = ele.attributeValue("close");
  32. if(StringUtils.isEmpty(index)) {
  33. index = "index";
  34. }
  35. if(StringUtils.isEmpty(separatorStr)) {
  36. separatorStr = ",";
  37. }
  38. if(StringUtils.isNotEmpty(openStr)) {
  39. currParams.put(Attrs.WHILE_OPEN,openStr);
  40. }
  41. if(StringUtils.isNotEmpty(closeStr)) {
  42. currParams.put(Attrs.WHILE_CLOSE,closeStr);
  43. }
  44. if(StringUtils.isNotEmpty(collectionStr)) {
  45. currParams.put(Attrs.WHILE_LIST,collectionStr);
  46. }
  47. currParams.put(Attrs.WHILE_SEPARATOR,separatorStr);
  48. if(index != null) {
  49. /**
  50. * 如果局部变量中存在当前循环变量的值,就表示已经不是第一次进入循环标签了,移除掉开始标记
  51. * 并将局部变量值加1
  52. */
  53. if(currParams.get(index) != null) {
  54. currParams.remove(Attrs.WHILE_START);
  55. currParams.put(index+"_", (Integer)currParams.get(index+"_") + 1);
  56. } else { //第一次进入循环标签内
  57. currParams.put(Attrs.WHILE_START,true);
  58. currParams.put(index+"_", 0);
  59. }
  60. currParams.put(index, (Integer)currParams.get(index+"_"));
  61. }
  62. boolean condition = true;
  63. Map<String, Object> allParams = getAllParams(currParams,globalParams);
  64. Object collection = null;
  65. if(StringUtils.isNotEmpty(collectionStr)) {
  66. //得到待循环的集合
  67. collection = Ognl.getValue(collectionStr,allParams);
  68. //如果集合属性不为空,但是条件为null则默认加上一个边界条件
  69. if(StringUtils.isEmpty(conditionStr)) {
  70. //这里只是用集合演示一下,也可以再加上数组,只不过改成.length而已
  71. if(collection instanceof List) {
  72. conditionStr = index+"_<"+collectionStr+".size()";
  73. } else if(collection instanceof Map){
  74. Map map = (Map)collection;
  75. Set set = map.entrySet();
  76. List list = new ArrayList(set);
  77. allParams.put("_list_", list);
  78. conditionStr = index+"_<_list_"+".size()";
  79. }
  80. }
  81. }
  82.  
  83. currParams.remove(Attrs.WHILE_END);
  84. if(StringUtils.isNotEmpty(conditionStr)) {
  85. //计算条件的值
  86. condition = (Boolean)Ognl.getValue(conditionStr,allParams);
  87. Map<String,Object> tempMap = new HashMap<>();
  88. tempMap.putAll(allParams);
  89. tempMap.put(index+"_",(Integer)currParams.get(index+"_") + 1);
  90. currParams.put(Attrs.WHILE_END,!(Boolean)Ognl.getValue(conditionStr,tempMap));
  91. }
  92.  
  93. boolean flag = true;
  94. currParams.put(Attrs.WHILE_INDEX, index);
  95. currParams.put(Attrs.WHILE_FLAG, true);
  96.  
  97. if(condition) {
  98. try {
  99. if(StringUtils.isNotEmpty(itemStr) && StringUtils.isNotEmpty(collectionStr)) {
  100. Object value = null;
  101. int idx = Integer.parseInt(currParams.get(index+"_").toString());
  102. if(collection instanceof List) {
  103. value = ((List)collection).get(idx);
  104. currParams.put(itemStr, value);
  105. } else if(collection instanceof Map){
  106. Map map = (Map)collection;
  107. Set<Map.Entry<String,Object>> set = map.entrySet();
  108. List<Map.Entry<String,Object>> list = new ArrayList(set);
  109. currParams.put(itemStr, list.get(idx).getValue());
  110. currParams.put(index, list.get(idx).getKey());
  111. }
  112.  
  113. }
  114. } catch (Exception e) {
  115. throw new Exception("从集合或者映射取值"+currParams.get(index)+"错误"+e.getMessage());
  116. }
  117.  
  118. } else {
  119. flag = false;
  120. destroyVars(currParams, index, itemStr);
  121. }
  122.  
  123. return flag;
  124. }
  125.  
  126. /**
  127. * 如果是第一次进入循环标签,则拼上open的内容
  128. */
  129. @Override
  130. public void pre(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele, StringBuilder sb) throws Exception {
  131. super.pre(currParams, globalParams, ele, sb);
  132. boolean start = MapUtils.getBoolean(currParams,Attrs.WHILE_START,false);
  133. if(start) {
  134. String open = MapUtils.getString(currParams,Attrs.WHILE_OPEN);
  135. sb.append(open);
  136. }
  137.  
  138. }
  139.  
  140. /**
  141. * 如果是最后进入循环标签,则最后拼上close的内容
  142. */
  143. @Override
  144. public void after(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele, StringBuilder sb) throws Exception {
  145. super.after(currParams, globalParams, ele, sb);
  146. boolean end = MapUtils.getBoolean(currParams,Attrs.WHILE_END,false);
  147. String separator = MapUtils.getString(currParams,Attrs.WHILE_SEPARATOR);
  148. if(!end && StringUtils.isNotEmpty(separator)) {
  149. sb.append(separator);
  150. }
  151. if(end) {
  152. String close = MapUtils.getString(currParams,Attrs.WHILE_CLOSE);
  153. if(sb.toString().endsWith(separator)) {
  154. sb.deleteCharAt(sb.length() - 1);
  155. }
  156. sb.append(close);
  157. }
  158. }
  159.  
  160. //释放临时变量
  161. private void destroyVars(Map<String, Object> currParams, String index,String varStr) {
  162. currParams.remove(Attrs.WHILE_INDEX);
  163. currParams.remove(Attrs.WHILE_FLAG);
  164. currParams.remove(Attrs.WHILE_SEPARATOR);
  165. currParams.remove(Attrs.WHILE_START);
  166. currParams.remove(Attrs.WHILE_END);
  167. currParams.remove(Attrs.WHILE_LIST);

    }

  1.  

}

  1.  
  1. import org.dom4j.Element;
  2.  
  3. import java.util.Map;
  4.  
  5. public class SqlNode extends BaseNode{
  6.  
  7. @Override
  8. public boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele,StringBuilder sb) throws Exception {
  9. return true;
  10. }
  11.  
  12. }
  1.  
  1. import java.util.Arrays;
  2. import java.util.List;
  3. import java.util.Map;
  4. import java.util.concurrent.ConcurrentHashMap;
  5.  
  6. /**
  7. * 节点工厂
  8. */
  9. public class NodeFactory {
  10.  
  11. private static Map<String,BaseNode> nodeMap = new ConcurrentHashMap<String,BaseNode>();
  12.  
  13. private final static List<String> whileList = Arrays.asList("foreach");
  14.  
  15. static {
  16. nodeMap.put("if", new IfNode());
  17. nodeMap.put("sql", new SqlNode());
  18. nodeMap.put("foreach", new ForeachNode());
  19. }
  20.  
  21. public static boolean isWhile(String elementName) {
  22. return whileList.contains(elementName);
  23. }
  24.  
  25. public static void addNode(String nodeName,BaseNode node) {
  26.  
  27. nodeMap.put(nodeName, node);
  28.  
  29. }
  30.  
  31. public static BaseNode create(String nodeName) {
  32.  
  33. return nodeMap.get(nodeName);
  34.  
  35. }
  36.  
  37. }
  1. /**
  2. * 各种标记
  3. * @author rongdi
  4. */
  5. public class Attrs {
  6.  
  7. public final static String TRANSACTIONAL = "transactional";
  8.  
  9. public final static String WHILE_START = "while-start";
  10.  
  11. public final static String WHILE_END = "while-end";
  12.  
  13. public final static String WHILE_OPEN = "while-open";
  14.  
  15. public final static String WHILE_CLOSE = "while-close";
  16.  
  17. public final static String WHILE_SEPARATOR = "while-separator";
  18.  
  19. public final static String WHILE_INDEX = "while-index";
  20.  
  21. public final static String WHILE_FLAG = "while-flag";
  22.  
  23. public final static String WHILE_LIST = "while-list";
  24.  
  25. public final static String WHEN_FLAG = "when-flag";
  26.  
  27. public static final String PROCESS_VAR = "process-var";
  28.  
  29. public final static String RESULT_FLAG = "result-flag";
  30.  
  31. public final static String RETURN_FLAG = "return-flag";
  32.  
  33. public final static String CONSOLE_VAR= "console-var";
  34.  
  35. public final static String DO = "do";
  36.  
  37. public final static String INDEX = "index";
  38.  
  39. public final static String CONDITION = "condition";
  40.  
  41. public final static String NAME= "name";
  42.  
  43. public final static String VALUE= "value";
  44.  
  45. public static final String TYPE = "type";
  46.  
  47. public static final String FORMAT = "format";
  48.  
  49. public static final String IF = "if";
  50.  
  51. public static final String ELSE = "else";
  52.  
  53. public final static String FILE= "file";
  54.  
  55. public static final String DATE = "date";
  56.  
  57. public static final String NOW = "now";
  58.  
  59. public static final String DECIMAL = "decimal";
  60.  
  61. public static final String ID = "id";
  62.  
  63. public static final String PARAMS = "params";
  64.  
  65. public static final String TARGET = "target";
  66.  
  67. public static final String SINGLE = "single";
  68.  
  69. public static final String PAGING = "paging";
  70.  
  71. public static final String DESC = "desc";
  72.  
  73. public static final String BREAK = "break";
  74.  
  75. public static final String CONTINUE = "continue";
  76.  
  77. public static final String COLLECTION = "collection";
  78.  
  79. public static final String VAR = "var";
  80.  
  81. public static final String EXECUTOR = "executor-1";
  82.  
  83. public static final String ROLLBACK_FLAG = "rollback-flag";
  84.  
  85. public static final String SERVICE = "service";
  86.  
  87. public static final String REF = "ref";
  88.  
  89. public static final String BIZS = "bizs";
  90.  
  91. public static final String TITLES = "titles";
  92.  
  93. public static final String COLUMNS = "columns";
  94.  
  95. public static final String CURRUSER = "currUser";
  96.  
  97. public static final String CURRPERM = "currPerm";
  98.  
  99. public static final String TASK_EXECUTOR = "taskExecutor";
  100.  
  101. public static final String DELIMITER = "delimiter";
  102.  
  103. public static final String OPERNAME = "operName";
  104.  
  105. }
  1. currParams.remove(varStr);
  2. currParams.remove(index);
  3. currParams.remove(index+"_");
  4. }
  5.  
  6. }

附上pom文件

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  3. <modelVersion>4.0.0</modelVersion>
  4. <groupId>com.rd</groupId>
  5. <artifactId>parser</artifactId>
  6. <packaging>jar</packaging>
  7. <version>1.0-SNAPSHOT</version>
  8. <name>myparser</name>
  9. <url>http://maven.apache.org</url>
  10.  
  11. <dependencies>
  12. <dependency>
  13. <groupId>dom4j</groupId>
  14. <artifactId>dom4j</artifactId>
  15. <version>1.6.1</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>opensymphony</groupId>
  19. <artifactId>ognl</artifactId>
  20. <version>2.6.11</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>commons-collections</groupId>
  24. <artifactId>commons-collections</artifactId>
  25. <version>3.2.1</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>commons-lang</groupId>
  29. <artifactId>commons-lang</artifactId>
  30. <version>2.6</version>
  31. </dependency>
  32. <dependency>
  33. <groupId>junit</groupId>
  34. <artifactId>junit</artifactId>
  35. <version>3.8.1</version>
  36. <scope>test</scope>
  37. </dependency>
  38. </dependencies>
  39.  
  40. <build>
  41. <resources>
  42. <resource>
  43. <directory>src/main/java</directory>
  44. <includes>
  45. <include>**/*.xml</include>
  46. </includes>
  47. </resource>
  48. <resource>
  49. <directory>src/main/resources</directory>
  50. <includes>
  51. <include>**/*</include>
  52. </includes>
  53. </resource>
  54. </resources>
  55. <testResources>
  56. <testResource>
  57. <directory>${project.basedir}/src/test/java</directory>
  58. </testResource>
  59. <testResource>
  60. <directory>${project.basedir}/src/test/resources</directory>
  61. </testResource>
  62. </testResources>
  63. <plugins>
  64. <plugin>
  65. <groupId>org.apache.maven.plugins</groupId>
  66. <artifactId>maven-compiler-plugin</artifactId>
  67. <version>3.1</version>
  68. <configuration>
  69. <source>1.8</source>
  70. <target>1.8</target>
  71. <encoding>UTF-8</encoding>
  72. </configuration>
  73. </plugin>
  74.  
  75. </plugins>
  76. </build>
  77.  
  78. </project>

自己动手实现mybatis动态sql的更多相关文章

  1. mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句

    mybatis 的动态sql语句是基于OGNL表达式的.可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:1. if 语句 (简单的条件判断)2. c ...

  2. 9.mybatis动态SQL标签的用法

    mybatis动态SQL标签的用法   动态 SQL MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么 ...

  3. Mybatis动态SQL单一基础类型参数用if标签

    Mybatis动态SQL单一基础类型参数用if标签时,test中应该用 _parameter,如: 1 2 3 4 5 6 <select id="selectByName" ...

  4. 超全MyBatis动态SQL详解!( 看完SQL爽多了)

    MyBatis 令人喜欢的一大特性就是动态 SQL. 在使用 JDBC 的过程中, 根据条件进行 SQL 的拼接是很麻烦且很容易出错的. MyBatis 动态 SQL 的出现, 解决了这个麻烦. My ...

  5. Mybatis动态SQL简单了解 Mybatis简介(四)

    动态SQL概况 MyBatis 的强大特性之一便是它的动态 SQL 在Java开发中经常遇到条件判断,比如: if(x>0){ //执行一些逻辑........ }   Mybatis应用中,S ...

  6. mybatis原理分析学习记录,mybatis动态sql学习记录

    以下个人学习笔记,仅供参考,欢迎指正. MyBatis 是支持定制化 SQL.存储过程以及高级映射的持久层框架,其主要就完成2件事情: 封装JDBC操作 利用反射打通Java类与SQL语句之间的相互转 ...

  7. mybatis 动态sql和参数

    mybatis 动态sql 名词解析 OGNL表达式 OGNL,全称为Object-Graph Navigation Language,它是一个功能强大的表达式语言,用来获取和设置Java对象的属性, ...

  8. MyBatis动态SQL之一使用 if 标签和 choose标签

    bootstrap react https://segmentfault.com/a/1190000010383464 xml 中 < 转义 to thi tha <if test=&qu ...

  9. MyBatis动态SQL(认真看看, 以后写SQL就爽多了)

    目录 0 一起来学习 mybatis 1 数据准备 2 if 标签 2.1 在 WHERE 条件中使用 if 标签 2.1.1 查询条件 2.1.2 动态 SQL 2.1.3 测试 2.2 在 UPD ...

随机推荐

  1. 使用ftp软件上传下载php文件时换行丢失bug(全部变为一行)

    文章来源:http://www.piaoyi.org/computer/ftp-php-r-n-bug.html 正 文: 在使用ftp软件上传下载php源文件时,我们偶尔会发现在本地windows下 ...

  2. ajax 轮询 和 php长连接

     只看加粗的字体 js   部分       1:  ajax 成功回调函数中 一定要用时间函数间隔调用  get_comment(). get_comments('init'); function ...

  3. Java常用类(四)之数组工具类Arrays

    前言 数组的工具类java.util.Arrays 由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作. 一.Arra ...

  4. python函数说明内容格式错误

    def levlTwo(levloneList,levlOneNum): """ 入参levloneList 一级城市列表 入参levlOneNum 用户选择的城市序号 ...

  5. eclipse中project facet问题

    一般出现在从别处import的项目上,只有项目文件夹上有红叉,其他地方都正常,现总结个人的几个解决方案: 有几种可能: 1,编码设置是否一致,也即是你项目原来的编码和现在eclipse用的默认编码是否 ...

  6. 用编写一个简单的记事本(C#实现)

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  7. 手工搭建ABP框架(1) - Web项目

    为了防止不提供原网址的转载,特在这里加上原文链接: http://www.cnblogs.com/skabyy/p/7295533.html ABP是 ASP.NET Boilerplate Proj ...

  8. JavaNIO阻塞IO添加服务器反馈

    package com.java.NIO; import java.io.IOException; import java.net.InetSocketAddress; import java.nio ...

  9. C# linq创建嵌套组

    以下示例演示如何在 LINQ 查询表达式中创建嵌套组. 首先根据学生年级创建每个组,然后根据每个人的姓名进一步细分为小组. public void QueryNestedGroups() { var ...

  10. http 状态

    用户如果向您的服务器发出了某项请求要求显示您网站上的某个网页(例如,当用户通过浏览器访问您的网页或在 Googlebot 抓取该网页时),那么,您的服务器会返回 HTTP 状态代码以响应该请求.此状态 ...