前言:
之前的SqlSession的运行过程还未讲完,还差一个SqlSession下的最重要的四大对象,这一篇文章将会讲解四大对象之间的关系.
SqlSession下的四大对象
之前讲解了映射器其实就是一个动态代理的对象,进入到MapperMethod的execute方法,那么进入到execute的方法又是如何执行的呢?肯定是通过我们所写的Sql语句,但是我们关心的不是这些细节,而是框架的设计这些,现在我们来分析这些对象.
1.Executor 执行器,用于调度其他对象
2.StatementHandler 调用statement执行操作
3.ParameterHandler 处理Sql参数
4.ResultHandler 处理最后的结果集封装并返回
一.Executor
用于与数据库进行交互,为我们提供了查询,更新,事务等方法,这和其他框架并无不同,那么先让我们看看Executor如何创建的把
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
他会根据你选择的三种执行器的哪一种去创建一个对象,创建对象后回去执行最后这句(Executor) interceptorChain.pluginAll(executor),其实这句就是mybatis的插件,它为我们构建了动态代理对象,然后在Executor执行之前先配置mybatis插件.
然后我们再从他的一个BaseExecutor中的updata再来进行分析
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
上面内容很显然Mybatis根据Configuration来构建StatementHandler,然后使用prepareStatement对SQL语句进行编译,它先调用了StatementHandler的prepare()方法进行预编译,然后通过StatementHandler的parameterize()方法来设置参数,resultHandler组装结果并返回结果,看到上面这段代码,我们的注意力就要从Executor中出来了,那么再让我们去分析一下StatementHandler这个对象吧.
二.StatementHandler
StatementHandler专门用来处理数据库之间的会话类,同样的我们来先看看Mybatis如何创建这个类的把.
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
这么几句话很显然创建的真实对象其实是RoutingStatementHandler只是实现了StatementHandler这个接口,那么它和Executor一样,都只是用代理对象进行了一步步的封装,他和Executor一样为我们提供了三种执行器分别时SimpleStatementHandler,PreparedStatementHandler,
CallableStatementHandler,他会根据上下文决定创建那个执行器
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());
}
}
这是RouingStatementHandler类下的构造方法,它提供了一个是配置delegate,他的作用是给实现类对象的使用提供一个统一,简易的适配器.再来看看他的主要三个方法之一的prepare,之后再按照顺序执行parameterize,query
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement);
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);
}
}
以上代码中的instantiateStatement方法是对SQL进行了预编译和基础配置然后调用parameterize方法去设置参数
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
继续查看query方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
现在就非常明确了,在得到DefaultSqlSession对象时初始化了Executor执行器。然后在调用Executor的doQuery方法查询的时候,会初始化StatementHandler对象,然后在初始化StatementHandler的过程中,会初始化ParameterHandler和ResultSetHandler对象,生成的StatementHandler对象会保存这两个对象的引用。直到这时,Executor的职责就差不多完成了,通过其他的三大组件来完成接下来的数据库查询工作并返回结果。
三.ParameterHandler
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
getParameterObject方法作用是返回参数对象,setParameter设置预编译SQL语句的参数.
那么我们来分析一下setParameter的实现吧.
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
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);
}
}
}
}
}
}
从parameterObject中取参数,根据typeHandler进行参数处理.typeHandler在Mybatis初始化的时候,就已经注册在了Configuration中.
四.ResultHandler
接口代码我就不贴出来了,根据前面的描述它只是对最终的结果进行了封装处理,我就不在这里多废话了.
总结:
SqlSession是通过Executor创建StatementHandler来运行,二StatementHandler要进过下面三步:
1.prepared 预编译
2.parameterize 设置参数
3. query 执行SQL.
以上就是SqlSession的执行过程,明白了四大对象是如何协作的了.