mybatis-sqlsource2
Posted siye1989
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis-sqlsource2相关的知识,希望对你有一定的参考价值。
2. SqlSource
org.apache.ibatis.mapping.SqlSource
,SQL 来源接口。它代表从 Mapper XML 或方法注解上,读取的一条 SQL 内容。代码如下:
// SqlSource.java
|
SqlSource 有多个实现类,如下图所示:
3. SqlSourceBuilder
org.apache.ibatis.builder.SqlSourceBuilder
,继承 BaseBuilder 抽象类,SqlSource 构建器,负责将 SQL 语句中的 #
替换成相应的 ?
占位符,并获取该 ?
占位符对应的 org.apache.ibatis.mapping.ParameterMapping
对象。
3.1 构造方法
// SqlSourceBuilder.java
|
- 为什么
parameterProperties
属性是这个值,答案在 《MyBatis 文档 —— Mapper XML 文件 —— 参数(Parameters)》
3.2 parse
// SqlSourceBuilder.java
|
<2>
处,创建 GenericTokenParser 对象。注意,传入的参数是#
和对。
<1>
处,创建 ParameterMappingTokenHandler 对象。<3>
处,调用GenericTokenParser#parse(String originalSql)
方法,执行解析。如果匹配到#
+对后,会调用 ParameterMappingTokenHandler 对应的
#handleToken(String content)
方法。详细解析,见 「3.3 ParameterMappingTokenHandler」 。<4>
处,创建 StaticSqlSource 对象。关于 StaticSqlSource 类,详细解析,见 「4.1 StaticSqlSource」 。
3.3 ParameterMappingTokenHandler
ParameterMappingTokenHandler ,实现 TokenHandler 接口,继承 BaseBuilder 抽象类,负责将匹配到的 #
和 对,替换成相应的
?
占位符,并获取该 ?
占位符对应的 org.apache.ibatis.mapping.ParameterMapping
对象。
3.3.1 构造方法
ParameterMappingTokenHandler 是 SqlSourceBuilder 的内部私有静态类。
// SqlSourceBuilder.java
|
3.3.2 handleToken
// SqlSourceBuilder.java
|
<1>
处,调用#buildParameterMapping(String content)
方法,构建 ParameterMapping 对象,并添加到parameterMappings
中。详细解析,见 「3.3.3 buildParameterMapping」 。<2>
处,返回?
占位符。- 如上两个步骤,就是 ParameterMappingTokenHandler 的核心。
3.3.3 buildParameterMapping
#buildParameterMapping(String content)
方法,构建 ParameterMapping 对象。代码如下:
// SqlSourceBuilder.java
|
-
<1>
处,调用#parseParameterMapping(String content)
方法,解析成 Map 集合。代码如下:// SqlSourceBuilder.java
private Map<String, String> parseParameterMapping(String content)
try
return new ParameterExpression(content);
catch (BuilderException ex)
throw ex;
catch (Exception ex)
throw new BuilderException("Parsing error was found in mapping #" + content + ". Check syntax #property|(expression), var1=value1, var2=value2, ... ", ex);
org.apache.ibatis.builder.ParameterExpression
类,继承 HashMap 类,负责参数表达式。感兴趣的胖友,可以自己看看。?? 艿艿暂时没细看。- 假设
content = "#age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler"
的结果如下图:
<2>
处,获得属性的名字和类型。<3>
处,创建 ParameterMapping.Builder 对象。<3.1>
处,初始化 ParameterMapping.Builder 对象的属性。<3.2>
处,如果typeHandlerAlias
非空,则获得对应的 TypeHandler 对象,并设置到 ParameterMapping.Builder 对象中。<3.3>
处,创建 ParameterMapping 对象。- 关于 ParameterMapping 类,胖友可以跳到 「5.1 ParameterMapping」 中看看。
4. SqlSource 的实现类
4.1 StaticSqlSource
org.apache.ibatis.builder.StaticSqlSource
,实现 SqlSource 接口,静态的 SqlSource 实现类。代码如下:
// StaticSqlSource.java
|
- StaticSqlSource 的静态,是相对于 DynamicSqlSource 和 RawSqlSource 来说呢。实际上,
StaticSqlSource.sql
属性,上面还是可能包括?
占位符。 #getBoundSql((Object parameterObject)
方法,创建 BoundSql 对象。通过parameterMappings
和parameterObject
属性,可以设置sql
上的每个占位符的值。例如:- 另外,我们在回过头看看 SqlSourceBuilder 类,它创建的也是 StaticSqlSource 对象。
下面,我们来看看下图的两段代码,胖友看看是否发现了什么规律:
- 如果是动态 SQL 的情况下,则创建 DynamicSqlSource 对象。
- 如果非动态 SQL 的情况下,则创建 RawSqlSource 对象。
下面,我们在「4.2」和「4.3」中,看看两者的区别。
4.2 DynamicSqlSource
org.apache.ibatis.scripting.xmltags.DynamicSqlSource
,实现 SqlSource 接口,动态的 SqlSource 实现类。代码如下:
// DynamicSqlSource.java
|
- 适用于使用了 OGNL 表达式,或者使用了
$
表达式的 SQL ,所以它是动态的,需要在每次执行#getBoundSql(Object parameterObject)
方法,根据参数,生成对应的 SQL 。 <1>
处,创建 DynamicContext 对象,并执行DynamicContext#apply(DynamicContext context)
方法,应用rootSqlNode
,相当于生成动态 SQL 。<2>
处,创建 SqlSourceBuilder 对象,并执行SqlSourceBuilder#parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters)
方法,解析出 SqlSource 对象。注意:- 返回的 SqlSource 对象,类型是 StaticSqlSource 类。
- 这个过程,会将
#
对,转换成对应的?
占位符,并获取该占位符对应的 ParameterMapping 对象。
<3>
处,调用StaticSqlSource#getBoundSql(Object parameterObject)
方法,获得 BoundSql 对象。<4>
处,从context.bindings
中,添加附加参数到 BoundSql 对象中。为什么要这么做?胖友回看下 《精尽 MyBatis 源码分析 —— SQL 初始化(上)之 SqlNode》 的 「6.7 ChooseSqlNode」 就明白了。<5>
处,返回 BoundSql 对象。
4.3 RawSqlSource
org.apache.ibatis.scripting.xmltags.RawSqlSource
,实现 SqlSource 接口,原始的 SqlSource 实现类。代码如下:
// RawSqlSource.java
|
- 适用于仅使用
#
表达式,或者不使用任何表达式的情况,所以它是静态的,仅需要在构造方法中,直接生成对应的 SQL 。 - 在构造方法中:
<1>
处,调用#getSql(Configuration configuration, SqlNode rootSqlNode)
方法,获得 SQL 。<2>
处,创建 SqlSourceBuilder 对象,并执行SqlSourceBuilder#parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters)
方法,解析出 SqlSource 对象。- 对应到 DynamicSqlSource ,就是
<1>
+<2>
了。
- 在
#getBoundSql(Object parameterObject)
方法中:<3>
处,调用StaticSqlSource#getBoundSql(Object parameterObject)
方法,获得 BoundSql 对象。- 对应到 DynamicSqlSource ,就是
<1>
+<2>
了。
这样,RawSqlSource 和 DynamicSqlSource 的区别,是不是就清晰了。
4.4 ProviderSqlSource
org.apache.ibatis.builder.annotation.ProviderSqlSource
,实现 SqlSource 接口,基于方法上的 @ProviderXXX
注解的 SqlSource 实现类。
4.4.1 构造方法
// ProviderSqlSource.java
|
- 参数比较多,但是灰常简单,胖友耐心的瞅瞅。
4.4.2 getBoundSql
// ProviderSqlSource.java
|
<1>
处,调用#createSqlSource(Object parameterObject)
方法,创建 SqlSource 对象。因为它是通过@ProviderXXX
注解的指定类的指定方法,动态生成 SQL 。所以,从思路上,和 DynamicSqlSource 是有点接近的。详细解析,见 「4.4.3 createSqlSource」 。<2>
处,调用SqlSource#getBoundSql(Object parameterObject)
方法,获得 BoundSql 对象。
4.4.3 createSqlSource
#createSqlSource(Object parameterObject)
方法,创建 SqlSource 对象。代码如下:
// ProviderSqlSource.java
|
-
<1>
处,获得 SQL 。-
<1.1>
处,调用#extractProviderMethodArguments(Object parameterObject)
方法,获得方法参数。代码如下:// ProviderSqlSource.java
private Object[] extractProviderMethodArguments(Object parameterObject)
if (providerContext != null)
Object[] args = new Object[2];
args[providerContextIndex == 0 ? 1 : 0] = parameterObject;
args[providerContextIndex] = providerContext;
return args;
else
return new Object[]parameterObject;
-
* 逻辑比较简单,胖友思考下。
* `<1.2>` 处,调用 `#extractProviderMethodArguments(Map<String, Object> params, String[] argumentNames)` 方法,获得方法参数。代码如下:
// ProviderSqlSource.java
|
* 逻辑比较简单,胖友思考下。 * 上面两个方法,无法理解的胖友,可以看看 `org.apache.ibatis.submitted.sqlprovider.Mapper` 和 `org.apache.ibatis.submitted.sqlprovider.OurSqlBuilder` 类。 * 调用 `#invokeProviderMethod(Object... args)` 方法,执行方法,生成 SQL 。代码如下:
// ProviderSqlSource.java
|
* 反射调用方法。
<2>
处,获得参数类型。-
<3>
处,调用#replacePlaceholder(String sql)
方法,替换掉 SQL 上的属性。代码如下:// ProviderSqlSource.java
private String replacePlaceholder(String sql)
return PropertyParser.parse(sql, configuration.getVariables());
<4>
处,调用SqlSourceBuilder#parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters)
方法,解析出 SqlSource 对象。- 代码比较长,胖友回过头自己再细看。?? 不过一般来说,MyBatis 注解使用较少,所以胖友也可以不用细看。
4.4.4 ProviderContext
org.apache.ibatis.builder.annotation.ProviderContext
,ProviderSqlSource 的上下文。代码如下:
// ProviderContext.java
|
5. BoundSql
org.apache.ibatis.mapping.BoundSql
,一次可执行的 SQL 封装。代码如下:
// BoundSql.java
|
5.1 ParameterMapping
org.apache.ibatis.mapping.ParameterMapping
,参数映射。代码如下:
// ParameterMapping.java
|
- 参数比较简单,胖友自己看看注释。可以忽略 ParameterMode 属性为
OUT
和INOUT
是在存储过程中使用的情况。 - 完整的该类,可点击 ParameterMapping 查看。
- 关于 ParameterMode 属性为
OUT
和INOUT
是在存储过程中使用的情况,可以看看 《Mybatis调用MySQL存储过程》 。当然,也可以不看,因为很少使用存储过程了。
5.2 ParameterMode
org.apache.ibatis.mapping.ParameterMode
,参数类型。代码如下:
// ParameterMode.java
|
- 只需要关注
IN
的情况。 - 另外,MyBatis 存储过程相关的源码,本系列会直接忽略。嘿嘿。
7. ParameterHandler
org.apache.ibatis.executor.parameter.ParameterHandler
,参数处理器接口。代码如下:
// ParameterHandler.java
|
7.1 DefaultParameterHandler
org.apache.ibatis.scripting.default.DefaultParameterHandler
,实现 ParameterHandler 接口,默认 ParameterHandler 实现类。
7.1.1 构造方法
// DefaultParameterHandler.java
|
7.1.2 setParameters
#setParameters(PreparedStatement ps)
方法,代码如下:
// DefaultParameterHandler.java
|
<1>
处,遍历 ParameterMapping 数组。<2>
处,获得 ParameterMapping 对象。<3>
处,获得值。有多种情况,胖友可以细看下。<4>
处,获得typeHandler
、jdbcType
属性。- 【重要】
<5>
处,调用TypeHandler#setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
方法,设置指定位置的?
占位符的参数。
以上是关于mybatis-sqlsource2的主要内容,如果未能解决你的问题,请参考以下文章