mybatis3.4.1源代码 - 对 <if>、${} 等标签的擦除

14 篇文章 0 订阅
1、使用ognl表达式,替换掉 ${...} 类型的值
2、根据调用数据《擦除》xml标签
3、替换占位符 井{..} 为 占位符 ?,同时解析出绑定的参数
4、创建 BoundSql 对象
org.apache.ibatis.scripting.xmltags.DynamicSqlSource

org.apache.ibatis.scripting.xmltags.TrimSqlNode
org.apache.ibatis.scripting.xmltags.IfSqlNode
org.apache.ibatis.scripting.xmltags.ForEachSqlNode
。。。。

 

package cn.java.demo.ibatis.internal.sqlnode;


import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.scripting.xmltags.*;
import org.apache.ibatis.session.Configuration;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.*;

/**
 */
@Slf4j
public class SqlNodeDemo {

    public static void main(String[] args) {
        Configuration configuration = new Configuration();
        configuration.setDatabaseId("");

        String xmlStr =
                "<update id=\"updateByExampleSelective\" parameterType=\"map\">\n" +
                        "    update orders\n" +
                        "    <set>\n" +
                        "      <if test=\"record.id != null\">\n" +
                        "        id = #{record.id,jdbcType=BIGINT},\n" +
                        "      </if>\n" +
                        "      <if test=\"record.userId != null\">\n" +
                        "        user_id = #{record.userId,jdbcType=BIGINT},\n" +
                        "      </if>\n" +
                        "      <if test=\"record.orderNo != null\">\n" +
                        "        order_no = #{record.orderNo,jdbcType=VARCHAR},\n" +
                        "      </if>\n" +
                        "      lock_version = lock_version + 1,\n" +
                        "      <if test=\"record.updatedAt != null\">\n" +
                        "        updated_at = #{record.updatedAt,jdbcType=TIMESTAMP},\n" +
                        "      </if>\n" +
                        "    </set>\n" +
                        "    <if test=\"_parameter != null\">\n" +
                        "      <where>\n" +
                        "        <foreach collection=\"example.oredCriteria\" item=\"criteria\" separator=\"or\">\n" +
                        "          <if test=\"criteria.valid\">\n" +
                        "            <trim prefix=\"(\" prefixOverrides=\"and\" suffix=\")\">\n" +
                        "              <foreach collection=\"criteria.criteria\" item=\"criterion\">\n" +
                        "                <choose>\n" +
                        "                  <when test=\"criterion.noValue\">\n" +
                        "                    and ${criterion.condition}\n" +
                        "                  </when>\n" +
                        "                  <when test=\"criterion.singleValue\">\n" +
                        "                    and ${criterion.condition} #{criterion.value}\n" +
                        "                  </when>\n" +
                        "                  <when test=\"criterion.betweenValue\">\n" +
                        "                    and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}\n" +
                        "                  </when>\n" +
                        "                  <when test=\"criterion.listValue\">\n" +
                        "                    and ${criterion.condition}\n" +
                        "                    <foreach close=\")\" collection=\"criterion.value\" item=\"listItem\" open=\"(\" separator=\",\">\n" +
                        "                      #{listItem}\n" +
                        "                    </foreach>\n" +
                        "                  </when>\n" +
                        "                </choose>\n" +
                        "              </foreach>\n" +
                        "            </trim>\n" +
                        "          </if>\n" +
                        "        </foreach>\n" +
                        "      </where>\n" +
                        "    </if>\n" +
                        "  </update>";

        /**
         * @see org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, java.lang.String, java.lang.Class)
         */
        XPathParser parser = new XPathParser(xmlStr, false, null, new XMLMapperEntityResolver());
        XNode contents = parser.evalNode("/update");

        /**
         * @see org.apache.ibatis.scripting.xmltags.XMLScriptBuilder
         */
        XMLScriptBuilder xmlScriptBuilder = new XMLScriptBuilder(configuration, contents);
        SqlSource sqlSource = xmlScriptBuilder.parseScriptNode();

        /**
         * 获取BoundSql
         */
        Map parameterObject = getParameterObject();
        sqlSource.getBoundSql(parameterObject);
    }

    static Map getParameterObject() {
        String parameterObjectStr = "{\"record\":{\"id\":1,\"userId\":1,\"orderNo\":\"orderNo_1\",\"updatedAt\":\"2020-01-01 01:01:01\"},\"example\":{\"oredCriteria\":[{\"valid\":true,\"criteria\":[{\"noValue\":true,\"condition\":\"id is not null\"},{\"singleValue\":true,\"condition\":\"userId = \",\"value\":\"1\"},{\"betweenValue\":true,\"condition\":\"id between\",\"value\":\"1\",\"secondValue\":\"2\"},{\"listValue\":true,\"condition\":\"order_no in\",\"value\":[\"1\",\"2\"]}]}]}}";
        Map parameterObject = JSON.parseObject(parameterObjectStr, Map.class);

        return parameterObject;
    }


    /**
     * @see org.apache.ibatis.scripting.xmltags.DynamicSqlSource
     */
    static class DynamicSqlSource implements SqlSource {
        private Configuration configuration;
        private SqlNode rootSqlNode;

        public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
            this.configuration = configuration;
            this.rootSqlNode = rootSqlNode;
        }

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            /**
             * 1、使用ognl表达式,替换掉 ${...} 类型的值
             * 2、根据调用数据《擦除》xml标签
             */
            DynamicContext context = new DynamicContext(configuration, parameterObject);
            rootSqlNode.apply(context);
            log.info("sql after do ognl, sql = {}", context.getSql());

            /**
             *  3、替换占位符 井{..} 为 占位符 ?,同时解析出绑定的参数
             */
            SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
            Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
            SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());

            /**
             * 4、创建 BoundSql 对象
             */
            BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
            for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
                boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
            }

            return boundSql;
        }
    }

    /**
     * @see org.apache.ibatis.scripting.xmltags.XMLScriptBuilder
     */
    static class XMLScriptBuilder {
        protected final Configuration configuration;
        private XNode context;
        private boolean isDynamic;

        public XMLScriptBuilder(Configuration configuration, XNode context) {
            this.configuration = configuration;
            this.context = context;
        }

        public SqlSource parseScriptNode() {
            List<SqlNode> contents = parseDynamicTags(context);
            MixedSqlNode rootSqlNode = new MixedSqlNode(contents);

            SqlSource sqlSource = null;
            sqlSource = new DynamicSqlSource(configuration, rootSqlNode);

            return sqlSource;
        }

        private List<SqlNode> parseDynamicTags(XNode node) {
            List<SqlNode> contents = new ArrayList<SqlNode>();
            NodeList children = node.getNode().getChildNodes();
            for (int i = 0; i < children.getLength(); i++) {
                XNode child = node.newXNode(children.item(i));
                if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
                    String data = child.getStringBody("");
                    TextSqlNode textSqlNode = new TextSqlNode(data);
                    if (textSqlNode.isDynamic()) { // 存在 ${} 占位符
                        contents.add(textSqlNode);
                        isDynamic = true;
                    } else {
                        contents.add(new StaticTextSqlNode(data));
                    }
                } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
                    // 解析标签
                    String nodeName = child.getNode().getNodeName();

                    // 节点处理器
                    NodeHandler handler = nodeHandlers(nodeName);
                    if (handler == null) {
                        throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
                    }
                    handler.handleNode(child, contents);
                    isDynamic = true;
                }
            }

            return contents;
        }

        NodeHandler nodeHandlers(String nodeName) {
            Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();
            map.put("trim", new TrimHandler());
            map.put("where", new WhereHandler());
            map.put("set", new SetHandler());
            map.put("foreach", new ForEachHandler());
            map.put("if", new IfHandler());
            map.put("choose", new ChooseHandler());
            map.put("when", new IfHandler());
            map.put("otherwise", new OtherwiseHandler());
            map.put("bind", new BindHandler());

            return map.get(nodeName);
        }

        private interface NodeHandler {
            void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
        }

        private class BindHandler implements NodeHandler {
            public BindHandler() {
                // Prevent Synthetic Access
            }

            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                final String name = nodeToHandle.getStringAttribute("name");
                final String expression = nodeToHandle.getStringAttribute("value");
                final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
                targetContents.add(node);
            }
        }

        private class TrimHandler implements NodeHandler {
            public TrimHandler() {
                // Prevent Synthetic Access
            }

            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                List<SqlNode> contents = parseDynamicTags(nodeToHandle);  // 递归
                MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
                String prefix = nodeToHandle.getStringAttribute("prefix");
                String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
                String suffix = nodeToHandle.getStringAttribute("suffix");
                String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
                TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
                targetContents.add(trim);
            }
        }

        private class WhereHandler implements NodeHandler {
            public WhereHandler() {
                // Prevent Synthetic Access
            }

            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                List<SqlNode> contents = parseDynamicTags(nodeToHandle); // 递归
                MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
                WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
                targetContents.add(where);
            }
        }

        private class SetHandler implements NodeHandler {
            public SetHandler() {
                // Prevent Synthetic Access
            }

            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                List<SqlNode> contents = parseDynamicTags(nodeToHandle);  // 递归
                MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
                SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
                targetContents.add(set);
            }
        }

        private class ForEachHandler implements NodeHandler {
            public ForEachHandler() {
                // Prevent Synthetic Access
            }

            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                List<SqlNode> contents = parseDynamicTags(nodeToHandle);  // 递归
                MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
                String collection = nodeToHandle.getStringAttribute("collection");
                String item = nodeToHandle.getStringAttribute("item");
                String index = nodeToHandle.getStringAttribute("index");
                String open = nodeToHandle.getStringAttribute("open");
                String close = nodeToHandle.getStringAttribute("close");
                String separator = nodeToHandle.getStringAttribute("separator");
                ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
                targetContents.add(forEachSqlNode);
            }
        }

        private class IfHandler implements NodeHandler {
            public IfHandler() {
                // Prevent Synthetic Access
            }

            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                List<SqlNode> contents = parseDynamicTags(nodeToHandle);
                MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
                String test = nodeToHandle.getStringAttribute("test");
                IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
                targetContents.add(ifSqlNode);
            }
        }

        private class OtherwiseHandler implements NodeHandler {
            public OtherwiseHandler() {
                // Prevent Synthetic Access
            }

            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                List<SqlNode> contents = parseDynamicTags(nodeToHandle);
                MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
                targetContents.add(mixedSqlNode);
            }
        }

        private class ChooseHandler implements NodeHandler {
            public ChooseHandler() {
                // Prevent Synthetic Access
            }

            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                List<SqlNode> whenSqlNodes = new ArrayList<SqlNode>();
                List<SqlNode> otherwiseSqlNodes = new ArrayList<SqlNode>();
                handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
                SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
                ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
                targetContents.add(chooseSqlNode);
            }

            private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {
                List<XNode> children = chooseSqlNode.getChildren();
                for (XNode child : children) {
                    String nodeName = child.getNode().getNodeName();
                    NodeHandler handler = nodeHandlers(nodeName);
                    if (handler instanceof IfHandler) {
                        handler.handleNode(child, ifSqlNodes);
                    } else if (handler instanceof OtherwiseHandler) {
                        handler.handleNode(child, defaultSqlNodes);
                    }
                }
            }

            private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
                SqlNode defaultSqlNode = null;
                if (defaultSqlNodes.size() == 1) {
                    defaultSqlNode = defaultSqlNodes.get(0);
                } else if (defaultSqlNodes.size() > 1) {
                    throw new BuilderException("Too many default (otherwise) elements in choose statement.");
                }
                return defaultSqlNode;
            }
        }
    }

}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值