0%

mybatis处理器

mybatis处理器

一、statementHandler

JDBC处理器,基于JDBC构建Statement并设置参数,然后去执行具体的SQL语句,每调用会话当中一次SQL,都会有与之相对应且唯一的Statement实例。

statementHandler.png

StatementHandler为顶层接口,为Statement处理器相关实现设置标准。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface StatementHandler {

// 预处理
Statement prepare(Connection connection, Integer transactionTimeout)throws SQLException;

// 设置参数
void parameterize(Statement statement) throws SQLException;

// 批处理
void batch(Statement statement) throws SQLException;

// 写操作
int update(Statement statement) throws SQLException;

// 读操作
<E> List<E> query(Statement statement, ResultHandler resultHandler)throws SQLException;

// 读游标
<E> Cursor<E> queryCursor(Statement statement)throws SQLException;

// 辅助接口:获取动态语句
BoundSql getBoundSql();

// 辅助接口:获取参数解析器
ParameterHandler getParameterHandler();

}

BaseStatementHandler抽取了具体子类的公共部分,比如有设置超时时间、设置获取行数等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// BaseStatementHandler#prepare
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// BaseStatementHandler的抽象方法,由具体的子类来实现
statement = instantiateStatement(connection);
// 根据子类返回的statement对象,来进行超时时间、获取行数的设置
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}

// 抽象方法,由具体的子类实现
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

本篇中没有特殊说明都是以PreparedStatementHandler为例。具体的子类需要实现这个方法,使用Conection对象生成对应的statement对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// PreparedStatementHandler#instantiateStatement
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
// 基于jdbc的connection#prepareStatement方法来创建PreparedStatement
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}

StatementHandler执行流程:

statementHandler执行过程.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// SimpleExecutor#doQuery
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 1.通过Configuration对象创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 2.预处理
stmt = prepareStatement(handler, ms.getStatementLog());
// 3.最后执行,对于PreparedStatement来说就是调用它的execute()方法。
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}

1.通过Configuration对象创建StatementHandler对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 通过RoutingStatementHandler导航类来根据需要不同类型的statement进行创建
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

// RoutingStatementHandler#new()
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}

}

2.预处理

1
2
3
4
5
6
7
8
9
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 调用StatementHandler的预处理接口来创建一个Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 这里会调用参数解析器DefaultPrepareterHander来对statement对象的参数值(会经过类型转换等操作)
handler.parameterize(stmt);
return stmt;
}

3.最后执行,对于PreparedStatement来说就是调用它的execute()方法。

1
2
3
4
5
6
7
8
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// PreparedStatement的execute()方法。
ps.execute();
// 执行完语句之后,使用结果集处理器对结果进行javabean的映射
return resultSetHandler.handleResultSets(ps);
}

BaseStatementHandler将具体的StatementHandler中编写了具体子类的共性部分,实际上是由它来进行具体的JDBC抽象,创建它需要执行器、参数解析器、结果集处理器、动态语句等的参与。

1
2
3
4
5
6
7
8
9
10
11
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;
protected final ParameterHandler parameterHandler;

protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;

protected BoundSql boundSql;

二、ParamenterHandler

参数名称解析器ParamNameResolver

单个参数:默认不做任何处理,除非设置了@Param

多个参数:

  1. 根据参数声明的顺序将参数转换为param1param2
  2. 通过@Param指定变量名称
  3. 基于反射转换成变量名,如果不支持则根据声明的顺序转换成arg0arg1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 单个参数情况下:
* args的值直接为这个参数的值 {0, "arg0"}
* 多参数情况下:
* 如果在mapper中对字段标记了@Param注解并设置了对应的字段名称,则names的值为 {{0, "field1"}, {1, "field2"}}
* 如果没有标记@Param注解,则names的值为{{0, "arg0"}, {1, "arg1"}}
* 所以这里仅仅是将字段名称和具体的参数值进行对应成param,实际的解析参数名的参数,在上层调用端。
*/
public Object getNamedParams(Object[] args) {
// names 是一个SortMap
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
// 如果没有@Param注解,并且参数个数为1,直接返回原值
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
// 直接将args参数中的值添加到param中
param.put(entry.getValue(), args[entry.getKey()]);

// 默认还会为参数根据声明的顺序设置param1, param2 ...
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// 不存在同名的情况下,可能有人字段名直接命名成paramX。
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
// 所以如果不出现同名的情况,假设mapper接口设置了两个参数,那么这里返回有两份参数,
// 一份要么是根据@Param解析规则,或者反射参数(启动参数) -parameters ,或者默认的arg0, arg1
// 一份是param1, param2...
// 要注意arg是从0下标开始,param是从1下标开始
return param;
}
}

所以根据返回param不同的类型,使用的方式也有所不同。

1
2
3
4
5
6
7
8
9
// 如果是单个参数,那么这里的#{userId},写什么都行,#{param1}, #{arg0}都可,实际上
// 根据就不校验名称,直接复制了
@Select({"select * from users where id=#{userId}"})
User selectByid(Integer id);

// 这里的#{name}也可以写成#{param1}, 但是注意不能写#{arg0}, 因为#{arg0}已经变成@param
// 注解中的名称
@Select({"select * from users where name=#{name} or age=#{user.age}"})
User selectByNameOrAge(@Param("name") String name, @Param("user") User user);

结合前文提到的StatementHandler执行流程时序图中,在预处理阶段,会为PrearedStatement设置参数,来具体看一下源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 以SimpleExecutor为例, 在调用doUpdate或者是doQuery方法创建StatementHandler之后,都会进行设置参数。
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 设置参数
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 创建statement
stmt = handler.prepare(connection, transaction.getTimeout());
// 为statement设置参数
handler.parameterize(stmt);
return stmt;
}

// PreparedStatementHandler#parameterize
@Override
public void parameterize(Statement statement) throws SQLException {
// 进入参数解析器,为statement设置参数
parameterHandler.setParameters((PreparedStatement) statement);
}

这里就进入到了ParameterHandlersetParameters中,为创建的preparedStatement设置具体的参数。先来看一下ParameterHandler的接口,ParameterHandlermybatis中的参数处理器,负责为PreparedStatementSQL语句参数动态赋值。

1
2
3
4
5
6
public interface ParameterHandler {
// 读取参数
Object getParameterObject();
// 用于对PreparedStatement的参数赋值
void setParameters(PreparedStatement ps) throws SQLException;
}

它只有一个实现类DefaultParameterHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 这个parameterObject就是前面说到通过ParameterNameSolver解析之后的对象
// 如果是单个参数,就是具体的值
// 如果是多个参数就是一个map对象
@Override
public Object getParameterObject() {
return parameterObject;
}

@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// parameterMappings是对具体执行语句的映射,将将要执行的语句映射成ParamterMapping对象,
// 将具体需要填充的参数#{}解析出来,形成参数列表
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
// 参数化sql就需要进行参数设置,遍历这个参数列表,将参数设置到PreparedStatement中
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// ParameterMode.OUT 存储过程的参数模式
if (parameterMapping.getMode() != ParameterMode.OUT) {
// 声明value,最后就是将这个值设置到PreparedStatement中
Object value;
// 获取执行语句声明的#{}中的参数变量名称
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
// 多个参数会走到这里,将map对象,转换成MetaObject对象,就可以直接通过字段名称来获取具体的值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 从parameterMapping获取TypeHandler类型转换器对象
TypeHandler typeHandler = parameterMapping.getTypeHandler();
// 从TypeHandler类型转换器中获取对应的数据库类型
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// TypeHandler.setParameter实际上最后会调用到PreParedStatement.set*()方法,为要执行的语句绑定参数值
// 比如说TypeHandler是BigDecimalTypeHandler 则调用的就是ps.setBigDecimal(i, parameter);
// TypeHandler是BigDecimalTypeHandler 则调用的就是ps.setByte(i, parameter);
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}

这里通过debugger了解一下ParameterMapping中的内容: 主要是将mybatisSQL或者是在实体上设置的一些配置进行映射,包含有字段名称、javaType数据类型、jdbcType数据库类型,typeHandler类型处理器等。

parameterMapping.png

三、ResultHandler

-------------本文结束感谢您的阅读-------------