30、SQL片段的解析

来源:互联网 发布:网络微营销 编辑:程序博客网 时间:2024/06/05 23:04

sqlFragment

可以翻译为sql片段,它的存在价值在于可复用sql片段,避免到处重复编写。

sqlFragment的解析过程

sqlFragment存储于Configuration内部。

protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

解析sqlFragment的过程非常简单。

org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XNode)方法部分源码。

    private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {        for (XNode context : list) {            String databaseId = context.getStringAttribute("databaseId");            String id = context.getStringAttribute("id");            id = builderAssistant.applyCurrentNamespace(id, false);            if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {                sqlFragments.put(id, context);            }        }    }

解析include标签的过程

org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode()方法源码。

    //解析导入的SQL片段        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);        includeParser.applyIncludes(context.getNode());

org.apache.ibatis.builder.xml.XMLIncludeTransformer.applyIncludes(Node, Properties)方法源码。

 private void applyIncludes(Node source, final Properties variablesContext, boolean included) {         //节点的名字是否是include         if (source.getNodeName().equals("include")) {             Node toInclude = findSqlFragment(getStringAttribute(source, "refid"), variablesContext);             Properties toIncludeContext = getVariablesContext(source, variablesContext);             //递归调用             applyIncludes(toInclude, toIncludeContext, true);             if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {                 toInclude = source.getOwnerDocument().importNode(toInclude, true);             }             // 将include节点,替换为sqlFragment节点             source.getParentNode().replaceChild(toInclude, source);             while (toInclude.hasChildNodes()) {                 // 将sqlFragment的子节点(也就是文本节点),插入到sqlFragment的前面                 toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude);             }             // 移除sqlFragment节点             toInclude.getParentNode().removeChild(toInclude);         } else if (source.getNodeType() == Node.ELEMENT_NODE) {//该节点是否是元素             NodeList children = source.getChildNodes();             for (int i = 0; i < children.getLength(); i++) {             //递归调用                 applyIncludes(children.item(i), variablesContext, included);             }         } else if (included && source.getNodeType() == Node.TEXT_NODE                 && !variablesContext.isEmpty()) {//该节点是否是内容,并且配置文件中配置有property属性             // 通过PropertyParser替换所有${xxx}占位符(attribute属性)             source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));         }     }

图示过程演示

以下内容为,摘自其他博客
- 解析节点

<select id="countAll" resultType="int">        select count(1) from (            <include refid="studentProperties"></include>        ) tmp</select>
  • include节点替换为sqlFragment节点
<select id="countAll" resultType="int">        select count(1) from (                <sql id="studentProperties">                    select                         stud_id as studId                        , name, email                        , dob                        , phone                    from students                </sql>        ) tmp</select>
  • 将sqlFragment的子节点(文本节点)insert到sqlFragment节点的前面。注意,对于dom来说,文本也是一个节点,叫TextNode。
<select id="countAll" resultType="int">        select count(1) from (                select                         stud_id as studId                        , name, email                        , dob                        , phone                    from students                <sql id="studentProperties">                    select                         stud_id as studId                        , name, email                        , dob                        , phone                    from students                </sql>        ) tmp</select>
  • 移除sqlFragment节点
    <select id="countAll" resultType="int">        select count(1) from (                select                         stud_id as studId                        , name, email                        , dob                        , phone                    from students        ) tmp</select>

小结

PropertyParser.parse(source.getNodeValue(), variablesContext);

会替换出现的#{XX},如果xx出现在property文件中,则替换相应的值