In the work, there is often such a requirement, for the same SQL condition query, first need to count the number of records, to calculate the pageCount, and then the results of the paging query display, see the following example.

<sql id="studentProperties"><! Select stud_id as studId, name, email, dob, phone from students </sql> <select id="countAll" resultType="int"> select count(1) from ( <include refid="studentProperties"></include><! TMP </select> <select ID ="findAll" resultType="Student" parameterType="map"> select * from (<include refid="studentProperties"></include><! - reuse - >) TMP limit # {offset}, # {pagesize} < / select >Copy the code

This is sqlFragment, it can be select | insert | update | delete label service, you can define many sqlFragment, then use the include introducing multiple sqlFragment tags. In the work, is also a more commonly used function, its advantages are obvious, reuse SQL fragment, its disadvantages are also obvious, can not fully show SQL logic, if a tag, include four to five sqlFragment, its readability is very poor.

SqlFragment content can be written at will, it does not need to be a full SQL, it can be as simple as “phone” text.

1. SqlFragment parsing process

SqlFragment is stored in the Configuration.

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

The process of parsing SQLFragments is very simple.

Org. Apache. Ibatis. Builder. XML. XMLMapperBuilder. ConfigurationElement method (XNode) part of the source code.

SqlElement (context.evalNodes("/mapper/ SQL ")); / / to select | insert | update service | delete buildStatementFromContext (context. EvalNodes (" select | insert | update | delete "));Copy the code

SqlFragment stored in Map<String, XNode> structure. Actually the key, is it how to select | insert | update | delete provide services.

2. Select | insert | update | delete tags, the process of resolving the include tag

Org. Apache. Ibatis. Builder. XML. XMLStatementBuilder. ParseStatementNode source () method.

// Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); / / focus on methods includeParser applyIncludes (context) getNode ()); // Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);Copy the code

Pre: and were parsed and removed Why remove it? The secrets are all hidden inside the applyIncludes() method.

Org. Apache. Ibatis. Builder. XML. XMLIncludeTransformer. ApplyIncludes source (Node, the Properties) method.

/** * Recursively apply includes through all SQL fragments. * @param source Include node in DOM tree * @param variablesContext Current context for static variables with values */ private void applyIncludes(Node source, final Properties variablesContext) { if (source.getNodeName().equals("include")) { // new full context for included SQL - contains inherited context and new variables from current include node Properties fullContext; String refid = getStringAttribute(source, "refid"); // replace variables in include refid value refid = PropertyParser.parse(refid, variablesContext); Node toInclude = findSqlFragment(refid); Properties newVariablesContext = getVariablesContext(source, variablesContext); if (! newVariablesContext.isEmpty()) { // merge contexts fullContext = new Properties(); fullContext.putAll(variablesContext); fullContext.putAll(newVariablesContext); } else { // no new context - use inherited fully fullContext = variablesContext; } // Recursively call applyIncludes(toInclude, fullContext); if (toInclude.getOwnerDocument() ! = source.getOwnerDocument()) { toInclude = source.getOwnerDocument().importNode(toInclude, true); } // Replace the include node with the sqlFragment node source.getparentNode ().replacechild (toInclude, source); While (toinclude.haschildNodes ()) {// Add a text node to the fragment, Insert toinclude.getparentNode ().insertBefore(toinclude.getFirstChild (), toInclude); } // Remove sqlFragment toinclude.getparentNode ().removechild (toInclude); } else if (source.getNodeType() == Node.ELEMENT_NODE) { NodeList children = source.getChildNodes(); for (int i=0; i<children.getLength(); I ++) {// Recursively call applyIncludes(children.item(I), variablesContext); } } else if (source.getNodeType() == Node.ATTRIBUTE_NODE && ! variablesContext.isEmpty()) { // replace variables in all attribute values source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext)); } else if (source.getNodeType() == Node.TEXT_NODE && ! variablesContext.isEmpty()) { // replace variables ins all text nodes source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext)); }}Copy the code

Above is the interpretation of the source code, in order to facilitate understanding, we next use the graphical approach to demonstrate its process.

3. Graphical process demonstration

① Parsing nodes

<select id="countAll" resultType="int">
    select count(1) from (
      <include refid="studentProperties"></include>
    ) tmp
  </select>
Copy the code

② Replace the include node with the sqlFragment node

<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>
Copy the code

③ Insert the child (text node) of the sqlFragment node before the sqlFragment node. Note that for the DOM, text is also a node, called a 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>
Copy the code

4 Remove the sqlFragment node

<select id="countAll" resultType="int">
    select count(1) from (
        select 
            stud_id as studId
            , name, email
            , dob
            , phone
          from students
) tmp
  </select>
Copy the code

⑤ The final result is shown in the figure

So TextNode1 + TextNode2 + TextNode3 make up a complete SQL. Select a value from TextNode, append the value from TextNode, append the value from TextNode, append the value from TextNode, append the value from TextNode, append the value from TextNode.

This is why the and node is removed.

This is Mybatis sqlFragment, the above example, are static SQL, i.e. static SQL.