首先了解一下statementHandler职责:主要负责处理MyBatis与JDBC之间Statement的交互,通俗而言就是负责操作Statement对象与数据库之间的交互。其执行过程中主要依赖ParameterHandler和ResultSetHandler进行参数绑定和结果实体类绑定。
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());
}
}
下面主要以查询为例,debugger源码层面分析。SQL执行开始皆从SqlSession开始。
@Override
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
//调用Executor中的query
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
/**
* 查询方法,专门提供select执行的方法
*/
@Override
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取查询SQL
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存的key,即作为HashMap中的key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//执行查询
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
此段代码块中涉及到BoundSQL对象,此处简单提一下相关概念,后期有时间专门总结一下BoundSQl.
BoundSQl对象主要是用于存储SQL语句,以及对应的参数相关对象。
继续调用BaseExcutor中的重载query方法:
/**
* 执行查询逻辑,
* 首先从缓存中获取数据,缓存中有数据则进行处理存储过程;
* 如果缓存中没有数据,则交互数据库查询数据,则将查询结果添加到缓存中
*/
@SuppressWarnings("unchecked")
@Override
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//如果不是嵌套查询,且动态查询语句中flushCache = true时即
继续跟进queryFromDatabase方法,该方法主要从数据库中查询数据
/**
* 交互数据库从数据库中查询数据,再把查询结果添加到缓存中
*/
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List list;
//向缓存中添加占位符,此时缓存中没有真正所需要的查询数据
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//查询数据库,由其子类实现,获取对应的查询数据
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//删除占位符
localCache.removeObject(key);
}
//再将从数据库查询的结果添加到一级缓存中
localCache.putObject(key, list);
//处理存储过程
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
继续跟进doQuery方法,发现BaseExecutor抽象类中该方法并没有实体,仅提供一个钩子方法,而是交给其子类实现,这里体现了模板设计模式。
/**交互数据库,查询数据*/
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
@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();
//Configuration中获取StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
到目前为止,我们发现了StatementHandler对象的来源自Configuration中newStatementHandler方法创建;
惊喜若现,继续跟进去;
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
最终,我们可以明确地发现StatementHandler对象是由其子类RoutingStatementHandler创建的,那么它创建的具体逻辑又是如何的呢?真相即将浮出水面,我们跟进RoutingStatementHandler的构造函数;
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());
}
}
看到一段很熟悉的代码,我们在文章开头已经将其展现出来了。
根据statementType的类型来判断是哪一种StatementHandler的实现,并且RoutingStatementHandler维护了一个delegate对象,通过delegate对象来实现对实际Handler对象的调用。这里涉及到了一个对象MappedStatement。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
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);
}
}
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// parameterMappings 就是对 #{} 或者 ${} 里面参数的封装
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
// 如果是参数化的SQL,便需要循环取出并设置参数的值
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 如果参数类型不是 OUT ,这个类型与 CallableStatementHandler 有关
// 因为存储过程不存在输出参数,所以参数不是输出参数的时候,就需要设置。
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 得到#{} 中的属性名
String propertyName = parameterMapping.getProperty();
// 如果 propertyName 是 Map 中的key
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
// 通过key 来得到 additionalParameter 中的value值
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
// 如果不是 additionalParameters 中的key,而且传入参数是 null, 则value 就是null
value = null;
}
// 如果 typeHandlerRegistry 中已经注册了这个参数的 Class对象,即它是Primitive 或者是String 的话
else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
// 否则就是 Map
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 在通过SqlSource 的parse 方法得到parameterMappings 的具体实现中,我们会得到parameterMappings的typeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
// 获取typeHandler 的jdbc type
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
@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();
//Configuration中获取StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
后面再次调用query方法,即SampleStatementHandler,PrepareStatementHandler,CallableStatementHandler中方法执行结果实体类绑定,这个具体过程解析,后面计划专门有文章总结。
经历上面的跟踪源码,我们可以了解到StatementHandler对象具体的创建过程,以及参数和结果绑定的流程。
新手跟踪源码,若存在错误或者不足之处,希望大佬及时指正!最后,希望大家多多支持,转发,点赞,关注,谢谢。