• 自己动手实现mybatis动态sql


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

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

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

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

    delete from pl_pagewidget
    <if test="widgetcodes != null">
        where pagewidgetcode in
       <foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
        <if test="index == 0">
            #{item}
        </if>
        <foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
                #{b}
         </foreach>
       </foreach>
    </if>
    <if test="a != null">
        and a = #{a}
    </if>

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

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

      ognl表达式使用很方便如下

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

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

    <sql>
       delete from pl_pagewidget
       <if test="widgetcodes != null">
        where pagewidgetcode in
        <foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
          <if test="index == 0">
            #{item}
          </if>
          <foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
                #{b}
          </foreach>
        </foreach>
       </if>
     <if test="a != null">
        and a = #{a}
     </if>
    </sql>

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

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

    import java.io.StringReader;
    import java.text.SimpleDateFormat;
    import java.util.Arrays;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    import org.apache.commons.collections.MapUtils;
    import org.apache.commons.lang.StringUtils;
    import org.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.Node;
    import org.dom4j.Text;
    import org.dom4j.io.SAXReader;
    
    import com.rd.sql.Attrs;
    import com.rd.sql.BaseNode;
    import com.rd.sql.NodeFactory;
    
    public class SqlParser {
    
        private Map<String,Object> currParams = new HashMap<String,Object>();
        
        /**
            delete from pl_pagewidget
            <if test="widgetcodes != null">
               where pagewidgetcode in
               <foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
                <if test="index == 0">
                    #{item}
                </if>
                <foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
                    #{b}
                </foreach>
               </foreach>
            </if>
            <if test="a != null">
               and a = #{a}
            </if>
         */
        public static void main(String[] args) throws Exception {
    
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("widgetcodes", Arrays.asList("1", "2"));
            map.put("bs", Arrays.asList("3", "4"));
            map.put("a", 1);
            SqlParser parser = new SqlParser();
            System.out
                    .println(parser.parser("delete from pl_pagewidget
    "
                                    + "	<if test="widgetcodes != null">
    "
                                    + "		where pagewidgetcode in
    "
                                    + "		<foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
    "
                                    + "		  <if test="index == 0">
    "
                                    + "		  #{item}
    "
                                    + "		  </if>
    "
                                    + "		  <foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
    "
                                    + "			#{b}
    " + "		  </foreach>
    "
                                    + "		</foreach>
    " + "	</if>
    "
                                    + "	<if test="a != null">
    "
                                    + "		and a = #{a}
    " + "	</if>
    ", map));
            System.out.println(parser.getParams());
    
        }
    
        public String parser(String xml, Map<String, Object> params)
                throws Exception {
            // xml = "<?xml version="1.0" encoding="UTF-8"?>"+xml;
            //给输入的动态sql套一层xml标签
            xml = "<sql>"+xml+"</sql>";
            SAXReader reader = new SAXReader(false);
            Document document = reader.read(new StringReader(xml));
            Element element = document.getRootElement();
            Map<String, Object> currParams = new HashMap<String, Object>();
            StringBuilder sb = new StringBuilder();
            //开始解析
            parserElement(element, currParams, params, sb);
            return sb.toString();
        }
    
        /**
         * 使用递归解析动态sql
         * @param ele1 待解析的xml标签
         * @param currParams
         * @param globalParams
         * @param sb
         * @throws Exception
         */
        private void parserElement(Element ele1, Map<String, Object> currParams,
                Map<String, Object> globalParams, StringBuilder sb)
                throws Exception {
    
            // 解析一个节点,比如解析到了一个if节点,假如test判断为true这里就返回true
            TempVal val = parserOneElement(currParams, globalParams, ele1, sb);
            //得到解析的这个节点的抽象节点对象
            BaseNode node = val.getNode();
            /**
             * 实际上这句之上的语句只是解析了xml的标签,并没有解析标签里的内容,这里
             * 表示要解析内容之前,如果有前置操作做一点前置操作
             */
            node.pre(currParams, globalParams, ele1, sb);
            //判断是否还需要解析节点里的内容,例如if节点test结果为true
            boolean flag = val.isContinue();
            // 得到该节点下的所有子节点的集合,包含普通文本
            List<Node> nodes = ele1.content();
            if (flag && !nodes.isEmpty()) {
                /**
                 * 这里表示要进一步解析节点里的内容了,可以把节点类比成一个方法的外壳
                 * 里面的内容类比成方法里的具体语句,开始解析节点的内容之前
                 * 先创建本节点下的局部参数的容器,最方便当然是map
                 */
                Map<String, Object> params = new HashMap<String, Object>();
                /**
                 * 把外面传进来的局部参数,直接放入容器,由于本例中参数都是常用数据类型
                 * 不会存在引用类型所以,这里相当于是一个copy,为了不影响外面传入的对象
                 * 可以类比方法调用传入参数的情况
                 */
                params.putAll(currParams);
                //循环所有子节点
                for (int i = 0; i < nodes.size();) {
                    Node n = nodes.get(i);
                    //如果节点是普通文本
                    if (n instanceof Text) {
                        String text = ((Text) n).getStringValue();
                        if (StringUtils.isNotEmpty(text.trim())) {
                            //处理一下文本,如处理#{xx},直接替换${yy}为真实传入的值
                            sb.append(handText(text, params,globalParams));
                        }
                        i++;
                    } else if (n instanceof Element) {
                        Element e1 = (Element) n;
                        // 递归解析xml子元素
                        parserElement(e1, params, globalParams, sb);
                        // 如果循环标志不为true则解析下一个标签
                        // 这里表示需要重复解析这个循环标签,则i不变,反之继续处理下一个元素
                        boolean while_flag = MapUtils.getBoolean(params,
                                Attrs.WHILE_FLAG, false);
                        if (!while_flag
                                || !NodeFactory.isWhile(n.getName())
                                || e1.attributeValue(Attrs.INDEX) == null
                                || !e1.attributeValue(Attrs.INDEX).equals(
                                        params.get(Attrs.WHILE_INDEX))) {
                            i++;
                        }
                    }
                }
                //节点处理之后做一些啥事
                node.after(currParams, globalParams, ele1, sb);
                // 回收当前作用域参数
                params.clear();
                params = null;
            }
    
        }
    
        /**
         * 处理文本替换掉#{item}这种参数
         * @param str
         * @param params
         * @return
         * @throws Exception
         */
        private String handText(String str, Map<String, Object> params,Map<String, Object> globalParams)
                throws Exception {
            //获取foreach这种标签中用于记录循环的变量
            String indexStr = MapUtils.getString(params, Attrs.WHILE_INDEX);
            Integer index = null;
            if(StringUtils.isNotEmpty(indexStr)) {
                index = MapUtils.getInteger(params, indexStr);
            }
            //匹配#{a}这种参数
            String reg1 = "(#\{)(\w+)(\})";
            //匹配${a}这种参数
            String reg2 = "(\$\{)(\w+)(\})";
            Pattern p1 = Pattern.compile(reg1);
            Matcher m1 = p1.matcher(str);
            Pattern p2 = Pattern.compile(reg2);
            Matcher m2 = p2.matcher(str);
            String whileList = MapUtils.getString(params, Attrs.WHILE_LIST);
            Map<String,Object> allParams = getAllParams(params, globalParams);
            while(m1.find()) {
                String tmpKey = m1.group(2);
                String key = whileList == null?tmpKey:(whileList+"_"+tmpKey);
                key = index == null?key:(key+index);
                String reKey = "#{"+key+"}";
                //如果在foreach类似的循环里,可能需要将参数#{xx}替换成#{xx_0},#{xx_1}
                str = str.replace(m1.group(0), reKey);
                currParams.put(key, allParams.get(tmpKey));
            }
            while(m2.find()) {
                String tmpKey = m2.group(2);
                Object value = allParams.get(tmpKey);
                if(value != null) {
                    str = str.replace(m2.group(0), getValue(value));
                }
            }
            return str;
        }
    
        private String getValue(Object value) {
            String result = "";
            if(value instanceof Date) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                result = sdf.format((Date)value);
            } else {
                result = String.valueOf(value);
            }
            return result;
        }
    
        private Map<String, Object> getAllParams(Map<String, Object> currParams,
                Map<String, Object> globalParams) {
            Map<String,Object> allParams = new HashMap<String,Object>();
            allParams.putAll(globalParams);
            allParams.putAll(currParams);
            return allParams;
        }
        
        // 解析一个xml元素
        private TempVal parserOneElement(Map<String, Object> currParams,
                Map<String, Object> globalParams, Element ele, StringBuilder sb)
                throws Exception {
            //获取xml标签名
            String eleName = ele.getName();
            //解析一个节点后是否继续,如遇到if这种节点,就需要判断test里是否为空
            boolean isContinue = false;
            //声明一个抽象节点
            BaseNode node = null;
            if (StringUtils.isNotEmpty(eleName)) {
                //使用节点工厂根据节点名得到一个节点对象比如是if节点还是foreach节点
                node = NodeFactory.create(eleName);
                //解析一下这个节点,返回是否还需要解析节点里的内容
                isContinue = node.parse(currParams, globalParams, ele, sb);
            }
            return new TempVal(isContinue, ele, node);
    
        }
    
        public Map<String, Object> getParams() {
            return currParams;
        }
    
        /**
         * 封装一个xml元素被解析后的结果
         * @author rongdi
         */
        final static class TempVal {
    
            private boolean isContinue;
    
            private Element ele;
    
            private BaseNode node;
    
            public TempVal(boolean isContinue, Element ele, BaseNode node) {
                this.isContinue = isContinue;
                this.ele = ele;
                this.node = node;
            }
    
            public boolean isContinue() {
                return isContinue;
            }
    
            public void setContinue(boolean isContinue) {
                this.isContinue = isContinue;
            }
    
            public Element getEle() {
                return ele;
            }
    
            public void setEle(Element ele) {
                this.ele = ele;
            }
    
            public BaseNode getNode() {
                return node;
            }
    
            public void setNode(BaseNode node) {
                this.node = node;
            }
    
        }
    }
    import org.dom4j.Element;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 抽象节点
     * @author rongdi
     */
    public abstract class BaseNode {
        public abstract boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele,StringBuilder sb) throws Exception;
        public void pre(Map<String, Object> currParams,Map<String, Object> globalParams,Element ele,StringBuilder sb) throws Exception {
        }
        public void after(Map<String, Object> currParams,Map<String, Object> globalParams,Element ele,StringBuilder sb) throws Exception {
        }
        protected Map<String, Object> getAllParams(Map<String, Object> currParams,
                                                   Map<String, Object> globalParams) {
            Map<String,Object> allParams = new HashMap<String,Object>();
            allParams.putAll(globalParams);
            allParams.putAll(currParams);
            return allParams;
        }
    }
    import java.util.Map;
    import ognl.Ognl;
    import org.apache.commons.lang.StringUtils;
    import org.dom4j.Element;
    
    /**
     * if节点
     * @author rongdi
     */
    public class IfNode extends BaseNode{
    
        @Override
        public boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele,StringBuilder sb) throws Exception {
            //得到if节点的test属性
            String testStr = ele.attributeValue("test");
            boolean test = false;
            try {
                if(StringUtils.isNotEmpty(testStr)) {
                    //合并全局变量和局部变量
                    Map<String, Object> allParams = getAllParams(currParams,globalParams);
                    //使用ognl判断true或者false
                    test = (Boolean) Ognl.getValue(testStr,allParams);
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new Exception("判断操作参数"+testStr+"不合法");
            }
    
            if(ele.content() != null && ele.content().size()==0) {
                test = true;
            }
    
            return test;
        }
    
    }
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import ognl.Ognl;
    import org.apache.commons.collections.MapUtils;
    import org.apache.commons.lang.StringUtils;
    import org.dom4j.Element;
    
    /**
      foreach节点属性如下
         collection 需要遍历的集合
         item 遍历集合后每个元素存放的变量
         index 遍历集合的索引数如0,1,2...
         separator 遍历后以指定分隔符拼接
         open 遍历后拼接开始的符号如 (
         close 遍历后拼接结束的符号如 )
     */
    public class ForeachNode extends BaseNode {
    
        @Override
        public boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele, StringBuilder sb) throws Exception {
    
            String conditionStr = null;
            String collectionStr = ele.attributeValue("collection");
            String itemStr = ele.attributeValue("item");
            String index = ele.attributeValue("index");
            String separatorStr = ele.attributeValue("separator");
            String openStr = ele.attributeValue("open");
            String closeStr = ele.attributeValue("close");
            if(StringUtils.isEmpty(index)) {
                index = "index";
            }
            if(StringUtils.isEmpty(separatorStr)) {
                separatorStr = ",";
            }
            if(StringUtils.isNotEmpty(openStr)) {
                currParams.put(Attrs.WHILE_OPEN,openStr);
            }
            if(StringUtils.isNotEmpty(closeStr)) {
                currParams.put(Attrs.WHILE_CLOSE,closeStr);
            }
            if(StringUtils.isNotEmpty(collectionStr)) {
                currParams.put(Attrs.WHILE_LIST,collectionStr);
            }
            currParams.put(Attrs.WHILE_SEPARATOR,separatorStr);
            if(index != null) {
                /**
                 * 如果局部变量中存在当前循环变量的值,就表示已经不是第一次进入循环标签了,移除掉开始标记
                 * 并将局部变量值加1
                 */
                if(currParams.get(index) != null) {
                    currParams.remove(Attrs.WHILE_START);
                    currParams.put(index+"_", (Integer)currParams.get(index+"_") + 1);
                } else { //第一次进入循环标签内
                    currParams.put(Attrs.WHILE_START,true);
                    currParams.put(index+"_", 0);
                }
                currParams.put(index, (Integer)currParams.get(index+"_"));
            }
            boolean condition = true;
            Map<String, Object> allParams = getAllParams(currParams,globalParams);
            Object collection = null;
            if(StringUtils.isNotEmpty(collectionStr)) {
                //得到待循环的集合
                collection = Ognl.getValue(collectionStr,allParams);
                //如果集合属性不为空,但是条件为null则默认加上一个边界条件
                if(StringUtils.isEmpty(conditionStr)) {
                    //这里只是用集合演示一下,也可以再加上数组,只不过改成.length而已
                    if(collection instanceof List) {
                        conditionStr = index+"_<"+collectionStr+".size()";
                    } else if(collection instanceof Map){
                        Map map = (Map)collection;
                        Set set = map.entrySet();
                        List list = new ArrayList(set);
                        allParams.put("_list_", list);
                        conditionStr = index+"_<_list_"+".size()";
                    }
                }
            }
    
            currParams.remove(Attrs.WHILE_END);
            if(StringUtils.isNotEmpty(conditionStr)) {
                //计算条件的值
                condition = (Boolean)Ognl.getValue(conditionStr,allParams);
                Map<String,Object> tempMap = new HashMap<>();
                tempMap.putAll(allParams);
                tempMap.put(index+"_",(Integer)currParams.get(index+"_") + 1);
                currParams.put(Attrs.WHILE_END,!(Boolean)Ognl.getValue(conditionStr,tempMap));
            }
    
            boolean flag = true;
            currParams.put(Attrs.WHILE_INDEX, index);
            currParams.put(Attrs.WHILE_FLAG, true);
    
            if(condition) {
                try {
                    if(StringUtils.isNotEmpty(itemStr) && StringUtils.isNotEmpty(collectionStr)) {
                        Object value = null;
                        int idx = Integer.parseInt(currParams.get(index+"_").toString());
                        if(collection instanceof List) {
                            value = ((List)collection).get(idx);
                            currParams.put(itemStr, value);
                        } else if(collection instanceof Map){
                            Map map = (Map)collection;
                            Set<Map.Entry<String,Object>> set = map.entrySet();
                            List<Map.Entry<String,Object>> list = new ArrayList(set);
                            currParams.put(itemStr, list.get(idx).getValue());
                            currParams.put(index, list.get(idx).getKey());
                        }
    
                    }
                } catch (Exception e) {
                    throw new Exception("从集合或者映射取值"+currParams.get(index)+"错误"+e.getMessage());
                }
    
            } else {
                flag = false;
                destroyVars(currParams, index, itemStr);
            }
    
            return flag;
        }
    
        /**
         * 如果是第一次进入循环标签,则拼上open的内容
         */
        @Override
        public void pre(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele, StringBuilder sb) throws Exception {
            super.pre(currParams, globalParams, ele, sb);
            boolean start = MapUtils.getBoolean(currParams,Attrs.WHILE_START,false);
            if(start) {
                String open = MapUtils.getString(currParams,Attrs.WHILE_OPEN);
                sb.append(open);
            }
    
        }
    
        /**
         * 如果是最后进入循环标签,则最后拼上close的内容
         */
        @Override
        public void after(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele, StringBuilder sb) throws Exception {
            super.after(currParams, globalParams, ele, sb);
            boolean end = MapUtils.getBoolean(currParams,Attrs.WHILE_END,false);
            String separator = MapUtils.getString(currParams,Attrs.WHILE_SEPARATOR);
            if(!end && StringUtils.isNotEmpty(separator)) {
                sb.append(separator);
            }
            if(end)  {
                String close = MapUtils.getString(currParams,Attrs.WHILE_CLOSE);
                if(sb.toString().endsWith(separator)) {
                    sb.deleteCharAt(sb.length() - 1);
                }
                sb.append(close);
            }
        }
    
        //释放临时变量
        private void destroyVars(Map<String, Object> currParams, String index,String varStr) {
            currParams.remove(Attrs.WHILE_INDEX);
            currParams.remove(Attrs.WHILE_FLAG);
            currParams.remove(Attrs.WHILE_SEPARATOR);
            currParams.remove(Attrs.WHILE_START);
            currParams.remove(Attrs.WHILE_END);
            currParams.remove(Attrs.WHILE_LIST);

        }

    
    

    }

     
    import org.dom4j.Element;
    
    import java.util.Map;
    
    public class SqlNode extends BaseNode{
    
        @Override
        public boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele,StringBuilder sb) throws Exception {
            return true;
        }
    
    }
    
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     *  节点工厂
     */
    public class NodeFactory {
        
        private static Map<String,BaseNode> nodeMap = new ConcurrentHashMap<String,BaseNode>();
    
        private final static List<String> whileList = Arrays.asList("foreach");
    
        static {
            nodeMap.put("if", new IfNode());
            nodeMap.put("sql", new SqlNode());
            nodeMap.put("foreach", new ForeachNode());
        }
    
        public static boolean isWhile(String elementName) {
            return whileList.contains(elementName);
        }
    
        public static void addNode(String nodeName,BaseNode node) {
            
            nodeMap.put(nodeName, node);
            
        }
        
        public static BaseNode create(String nodeName) {
            
            return nodeMap.get(nodeName);
            
        }
        
    }
    /**
     * 各种标记
     * @author rongdi
     */
    public class Attrs {
        
        public final static String TRANSACTIONAL = "transactional";
    
        public final static String WHILE_START = "while-start";
    
        public final static String WHILE_END = "while-end";
    
        public final static String WHILE_OPEN = "while-open";
    
        public final static String WHILE_CLOSE = "while-close";
    
        public final static String WHILE_SEPARATOR = "while-separator";
    
        public final static String WHILE_INDEX = "while-index";
        
        public final static String WHILE_FLAG = "while-flag";
        
        public final static String WHILE_LIST = "while-list";
        
        public final static String WHEN_FLAG = "when-flag";
        
        public static final String PROCESS_VAR = "process-var";
        
        public final static String RESULT_FLAG = "result-flag";
        
        public final static String RETURN_FLAG = "return-flag";
        
        public final static String CONSOLE_VAR= "console-var";
        
        public final static String DO = "do";
        
        public final static String INDEX = "index";
        
        public final static String CONDITION = "condition";
        
        public final static String NAME= "name";
        
        public final static String VALUE= "value";
    
        public static final String TYPE = "type";
    
        public static final String FORMAT = "format";
        
        public static final String IF = "if";
        
        public static final String ELSE = "else";
        
        public final static String FILE= "file";
        
        public static final String DATE = "date";
        
        public static final String NOW = "now";
        
        public static final String DECIMAL = "decimal";
        
        public static final String ID = "id";
        
        public static final String PARAMS = "params";
    
        public static final String TARGET = "target";
    
        public static final String SINGLE = "single";
        
        public static final String PAGING = "paging";
    
        public static final String DESC = "desc";
    
        public static final String BREAK = "break";
        
        public static final String CONTINUE = "continue";
        
        public static final String COLLECTION = "collection";
    
        public static final String VAR = "var";
        
        public static final String EXECUTOR = "executor-1";
        
        public static final String ROLLBACK_FLAG = "rollback-flag";
        
        public static final String SERVICE = "service";
        
        public static final String REF = "ref";
    
        public static final String BIZS = "bizs";
    
        public static final String TITLES = "titles";
        
        public static final String COLUMNS = "columns";
        
        public static final String CURRUSER = "currUser";
        
        public static final String CURRPERM = "currPerm";
        
        public static final String TASK_EXECUTOR = "taskExecutor";
    
        public static final String DELIMITER = "delimiter";
    
        public static final String OPERNAME = "operName";
    
        
    }
    
            currParams.remove(varStr);
            currParams.remove(index);
            currParams.remove(index+"_");
        }
    
    }

    附上pom文件

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.rd</groupId>
        <artifactId>parser</artifactId>
        <packaging>jar</packaging>
        <version>1.0-SNAPSHOT</version>
        <name>myparser</name>
        <url>http://maven.apache.org</url>
    
        <dependencies>
            <dependency>
                <groupId>dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>1.6.1</version>
            </dependency>
            <dependency>
                <groupId>opensymphony</groupId>
                <artifactId>ognl</artifactId>
                <version>2.6.11</version>
            </dependency>
            <dependency>
                <groupId>commons-collections</groupId>
                <artifactId>commons-collections</artifactId>
                <version>3.2.1</version>
            </dependency>
            <dependency>
                <groupId>commons-lang</groupId>
                <artifactId>commons-lang</artifactId>
                <version>2.6</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>3.8.1</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                    </includes>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*</include>
                    </includes>
                </resource>
            </resources>
            <testResources>
                <testResource>
                    <directory>${project.basedir}/src/test/java</directory>
                </testResource>
                <testResource>
                    <directory>${project.basedir}/src/test/resources</directory>
                </testResource>
            </testResources>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
    
            </plugins>
        </build>
    
    </project>
  • 相关阅读:
    Bootstrap标签(label)的使用
    Docker学习(二)
    linux 的tee命令
    解决 Docker pull 出现的net/http: TLS handshake timeout 的一个办法
    win 10 安装.msi 程序出现the error code is 2503
    Kbuntu16.04利用快捷键调用终端Konsole
    ubuntu上swift开发学习2
    ubuntu上swift开发学习1
    Linux中常用文件传输命令及使用方法
    Kbuntu16.04添加工作空间
  • 原文地址:https://www.cnblogs.com/rongdi/p/8013151.html
Copyright © 2020-2023  润新知