Mybatis handles sql statements

In this paper, the processing of sql statements by mybatis is mainly divided into compile time processing and execution time processing
In addition, sql statements are divided into static sql statements and dynamic sql statements, which are distinguished by parameter modifiers.
Static sql statement: no parameters or parameter modifiers are all #{} such, and there are no other tags
Dynamic sql statement: one of the parameters is decorated with ${} or labeled
Mybatis definition of dynamic sql

After the initialization of mybatis, SqlSource exists for all processed sql statements
Configuration —> MappedStatement–> SqlSource

Compile time processing

The processing of statements during compilation is mainly divided into pre compilation and non pre compilation, and splicing dynamic sql statements.

precompile

Precompiling mainly operates on static sql statements. Replace #{xx} with? Placeholder for.
Original sql statement

select * from user_bo ur where ur.id = #{ id} and ur.user_name = #{name }

Finished processing

select * from user_bo ur where ur.id = ? and ur.user_name = ?

Non precompiled

Original sql statement

select * from user_bo ur where ur.id = #{ id} and ur.user_name = ${name }

Finished processing

select * from user_bo ur where ur.id = #{ id} and ur.user_name = ${name }

Splicing dynamic sql statements

Splicing dynamic sql statements is mainly to deal with the labels of dynamic sql.
Original sql statement

    select * from user_bo ur
    <where>
      <if test="id!=null">
        and ur.id = #{ id}
      </if>
      <if test="name!=null">
        and ur.user_name = #{name }
      </if>
    </where>

Finished processing

select * from user_bo ur where ur.id = #{ id} and ur.user_name = #{name }

perhaps
Original sql statement

    select * from user_bo ur
    <where>
      <if test="id!=null">
        and ur.id = ${ id}
      </if>
      <if test="name!=null">
        and ur.user_name = #{name }
      </if>
    </where>

Finished processing

select * from user_bo ur where ur.id = #{ id} and ur.user_name = ${name }

Processing during operation

The processing of statements during operation is mainly divided into: sql parameter assignment
The parameter assignment during operation is mainly the encapsulated native JDBC operation.

Source code analysis

Node: both static sql statements and dynamic sql statements are a node set after parsing. SqlNode objects are used to replace the code types, and there are many types of nodes, such as WhereSqlNode, IfSqlNode, etc

Placeholder replacement during precompiling #{}

The main processes of static sql statements that can meet placeholder replacement #{} during precompiling are

  public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    String sql;
    if (configuration.isShrinkWhitespacesInSql()) {
      sql = parser.parse(removeExtraWhitespaces(originalSql));
    } else {
      sql = parser.parse(originalSql);
    }
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }

The parse(...) method of GenericTokenParser is mainly completed through the interception and replacement of Java String.
What is returned is a StaticSqlSource static sql resource

Dynamic node analysis

Take the following as an example

  <select id="selectAll03" parameterType="u" resultType="u">
    select * from user_bo ur
    <where>
      <if test="id!=null">
        and ur.id = #{ id}
      </if>
      <if test="name!=null">
        and ur.user_name = #{name }
      </if>
    </where>
  </select>

Let's look at the flow chart first

From parseDynamicTags to parsing the select node
The script attribute in the XMLScriptBuilder object is the node information, and many different NodeHandler node processors were initialized in the previous step
XMLScriptBuilder-context-body: select * from user_bo ur
script:

<select parameterType="u" id="selectAll03" resultType="u">
    <where>
        <if test="id!=null">
        and ur.id = #{ id}
      </if>
        <if test="name!=null">
        and ur.user_name = #{name }
      </if>
    </where>
</select>

Source code to start parsing:

SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

langDriver.createSqlSource:

public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
  XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
  return builder.parseScriptNode();
}

builder.parseScriptNode():

public SqlSource parseScriptNode() {
  MixedSqlNode rootSqlNode = parseDynamicTags(context);
  SqlSource sqlSource;
  if (isDynamic) {
    sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
  } else {
    sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
  }
  return sqlSource;
}

parseDynamicTags(context):

  protected MixedSqlNode parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<>();
    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 = nodeHandlerMap.get(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return new MixedSqlNode(contents);
  }

if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE)

Get body: select * from user_bo ur is not a node
Use text type node decoration

TextSqlNode textSqlNode = new TextSqlNode(data);

Is it a dynamic type
textSqlNode.isDynamic():

  public boolean isDynamic() {
    DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
    GenericTokenParser parser = createParser(checker);
    parser.parse(text);
    return checker.isDynamic();
  }

Judge whether there is ${xxx}, so it is not dynamic

  private GenericTokenParser createParser(TokenHandler handler) {
    return new GenericTokenParser("${", "}", handler);
  }

Convert the sql node into static text and store it in the List contents node collection

contents.add(new StaticTextSqlNode(data));

Continue looping layer 2 nodes
XNode child:

<where>
    <if test="id!=null">
        and ur.id = #{ id}
      </if>
    <if test="name!=null">
        and ur.user_name = #{name }
      </if>
</where>

This is a node object, so it will go nodeType = 1

child.getNode().getNodeType() == Node.ELEMENT_NODE

Select the node processor by node name

NodeHandler handler = nodeHandlerMap.get(nodeName);

Processing node

handler.handleNode(child, contents);

This node is where and the last node. There are nodes in where
Enter WhereHandler first

Keywords: Java Mybatis

Added by digibrain on Sat, 25 Dec 2021 22:44:33 +0200