1. Mybatis support dynamic SQL and basic usage

Org. Apache. Ibatis. Scripting. Xmltags. XMLScriptBuilder. NodeHandlers (String) part of the source code.

  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);
  }
Copy the code

Mybatis supported by dynamic SQL tags: trim | where | set | foreach | if | choose | s | otherwise | bind.

<select id="findStudents" parameterType="customMap" resultType="StudentResult"> select * from STUDENTS where 1 = 1 <choose> <when test="name ! = null"> and name = #{name} </when> <when test="email ! = null"> and EMAIL = #{email} </when> <otherwise> and PHONE = "123" </otherwise> </choose> </select> <select id="countAll" resultType="int"> select count(1) from ( select stud_id as studId , name, email , dob , phone from students <where> <if test="id ! = null"> AND STUD_ID &lt; 310 </if> </where> ) tmp </select> <select id="findAllStudents" resultMap="StudentResult" parameterMap="customMap"> <bind name="status" value="'status'"/> SELECT * FROM STUDENTS WHERE STUD_ID > #{id}, #{status},${driver} </select> <insert id="insertStudents" useGeneratedKeys="true" keyProperty="studId" parameterType="java.util.ArrayList"> INSERT INTO STUDENTS(STUD_ID, NAME, EMAIL, DOB, PHONE) VALUES <foreach collection="list" item="item" index="index" separator=","> (#{item.studId},#{item.name},#{item.email},#{item.dob}, #{item.phone}) </foreach> </insert>Copy the code

In order to avoid the length is too long, we simply enumerates several dynamic SQL basic usage, our focus is still to analyze Mybatis dynamic SQL underlying design principle.

2. SqlSource

In Mybatis, each a select | insert | update | delete labels, will be resolved as a MappedStatement object, SqlSource MappedStatement is an attribute in the object, The SQL string that it ultimately executes is provided by the SqlSource.

public final class MappedStatement {
    private SqlSource sqlSource;
}
Copy the code

Org. Apache. Ibatis. Builder. XML. XMLStatementBuilder. ParseStatementNode () part of the source code:

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

Made In IntelliJ Idea IDE

DynamicSqlSource: Processes dynamic SQL.

RawSqlSource: Handles static SQL, its interior decorates StaticSqlSource.

StaticSqlSource: Processing static SQL, whether it is dynamic or static.

ProviderSqlSource: Handles SQL in the form of annotations.

The main difference between DynamicSqlSource and StaticSqlSource is: DynamicSqlSource String SQL can be used directly. DynamicSqlSource String SQL must be parsed and spliced into the final SQL.

DynamicSqlSource and SqlNode

public class DynamicSqlSource implements SqlSource {

  private Configuration configuration;
  private SqlNode rootSqlNode;
}

public interface SqlNode {
  boolean apply(DynamicContext context);
}
Copy the code

Boolean apply (DynamicContext context) : DynamicContext appends the result of an SQL process to a DynamicContext. DynamicContext is a StringBuilder object that evaluates SQL fragments and appends them together. Form the final SQL. It is important to understand this method so that you can truly understand how a complete SQL is assembled.

The following pseudocode shows the core principles of sqlNode. apply(DynamicContext) method design.

        StringBuilder sb = new StringBuilder();
		IfSqlNode.apply(StringBuilder sb) {
			sb.append("select ");
		}
		SetSqlNode.apply(StringBuilder sb) {
			sb.append("* from ss ");
		}
		sb.toString();
		//output: select * from ss
Copy the code

DynamicSqlSource is the DynamicSqlSource, and SqlNode represents the specific dynamic SQL type in the DynamicSqlSource.

Made In IntelliJ Idea IDE

SqlNode (SqlNode, SqlNode, SqlNode, SqlNode, SqlNode)

VarDeclSqlNode: SqlNode class that handles dynamic SQL tags.

public class VarDeclSqlNode implements SqlNode { private final String name; private final String expression; public VarDeclSqlNode(String var, String exp) { name = var; expression = exp; } @Override public boolean apply(DynamicContext context) { final Object value = OgnlCache.getValue(expression, context.getBindings()); Context. Bind (name, value); return true; }}Copy the code

MixedSqlNode: stands for MixedSqlNode, which holds a collection of other sqlnodes and can be thought of as a List, which it is.

The SqlNode rootSqlNode property in DynamicSqlSource, which is usually a MixedSqlNode object (or a StaticTextSqlNode for purely static SQL), MixedSqlNode objects hold all List collections, which is why all SQLNodes can be found in a rootSqlNode.

4. SqlNode combination design mode

public class ForEachSqlNode implements SqlNode {
 private SqlNode contents;
}
Copy the code

SqlNode, uses the combined design pattern, the combined design pattern can be used to represent the classic tree structure, some people can not help but ask, the combined design pattern, its properties, should be a List collection, how can be a single SqlNode?

MixedSqlNode represents a List collection, so it’s a classic composite design pattern.

For example, inside a ForEachSqlNode, it might be a StaticTextSqlNode, which you can easily see in the XML.

<foreach collection="list" item="item" index="index" separator=","> 
        	(#{item.studId},#{item.name},#{item.email},#{item.dob}, #{item.phone}) 
</foreach>
Copy the code

5. NodeHandler

Sqlnodes are created by NodeHandler.

(Made In EDrawMax)

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); }}} / /... }Copy the code

When ChooseSqlNode is created, the logic for “when” and “otherwise” is created, and “When” translates to “if” tag processing and “otherwise” translates to “SqlNode” processing. Typically a StaticTextSqlNode.

map.put("if", new IfHandler());
map.put("when", new IfHandler());
Copy the code

Due to space problems, we do not describe each one, readers can check.

6. LanguageDriver

LanguageDriver is a helper class for creating AN SqlSource.

(Made In IntelliJ Idea IDE) XMLLanguageDriver: Used to create dynamic and static SqlSource.

RawLanguageDriver: This parameter can be used to ensure only static SQL. It must not contain any dynamic SQL content. Otherwise, use XMLLanguageDriver. It simply does a static SQL check on the result created by XMLLanguageDriver and throws an exception if it finds dynamic SQL.

/**
 * As of 3.2.4 the default XML language is able to identify static statements
 * and create a {@link RawSqlSource}. So there is no need to use RAW unless you
 * want to make sure that there is not any dynamic tag for any reason.
 * 
 * @since 3.2.0
 * @author Eduardo Macarron
 */
public class RawLanguageDriver extends XMLLanguageDriver {

  @Override
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    SqlSource source = super.createSqlSource(configuration, script, parameterType);
    checkIsNotDynamic(source);
    return source;
  }
// ...
}
Copy the code

The last

Thank you for reading here, the article is inadequate, welcome to point out; If you think it’s good, give me a thumbs up.