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