SqlSession
映射器通过动态代理进入到了MapperMethod的execute方法,然后通过判断进入了SqlSession的delete、update、insert、select等方法
SqlSession通过Executor、StatementHandler、ParameterHandler和ResultSetHandler来完成数据库操作和结果返回的
Executor执行器,由它调度StatementHandler、ParameterHandler、ResultSetHandler等来执行对应的SQL。其中StatementHandler最重要
StatementHandler的作用是使用数据库的Statement(PreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用,许多重要的插件都是通过拦截它来实现的。
ParameterHandler用来处理SQL参数
ResultSetHandler是进行数据集(ResultSet)的封装返回处理的,它相当复杂,不常用
Executor
SqlSession是一个门面,真正干活的是执行器,它是一个真正执行Java和数据库交互的对象,很重要。MyBatis中有3种执行器,可以在MyBatis的配置文件中选择
SIMPLE——简易执行器,没什么特别的,不配置就是默认执行器
REUSE——能够执行重用预处理语句的执行器
BATCH——执行器重用语句和批量更新,批量专用的执行器
执行器提供了查询(query)方法、更新(update)方法和相关的事务方法,和其他框架并不不同。
创建Executor,在Configuration类中
package org.apache.ibatis.session;
public class Configuration {
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;
}
}
MyBatis将根据配置类型去确定需要创建哪一种Executor,它的缓存则用CachingExecutor进行包装Executor。在运用插件时,拦截Executor就有可能获取这样的一个对象了,在创建对象之后,回去执行这样的一行代码。
interceptorChain.pluginAll(executor);
这就是MyBatis的插件,将构建一层层的动态代理对象 ,我们可以修改在调度真实的Executor方法之前执行配置插件的代码,这就是插件的原理。
SimpleExecutor执行器的query方法
package org.apache.ibatis.executor;
public class SimpleExecutor extends BaseExecutor {
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.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
}
MyBatis根据Configuration来构建StatementHandler,然后使用prepareStatement方法,对SQL编译和参数进行初始化。实现:调用了StatementHandler的prepare()进行了预编译和基础的设置,然后通过StatementHandler的parametersize()来设置参数,最后使用StatementHandler的query方法,把ResultHandler传递进去,使用它组织结果返回给调用者完成一次查询,现在主要看StatementHandler。
StatementHandler——数据库会话器
数据库会话器专门处理数据库会话的。
生成StatementHandler
package org.apache.ibatis.session;
public class Configuration {
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一样,用代理对象做一层层的封装
RoutingStatementHandler不是真实的服务对象,它通过适配模式来找到对应的StatementHandler来执行。在MyBatis中,和Executor一样,RoutingStatementHandler分为3种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。所对应的是JDBC的Statement、PreparedStatement(预编译处理)和CallableStatement(存储过程处理)
在初始化RoutingStatementHandler对象时,它会根据上下文环境决定创建哪个具体的StatementHandler对象实例。
package org.apache.ibatis.executor.statement;
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
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());
}
}
}
它定义了一个对象的配适器——delegate,是一个StatementHandler接口对象,然后构造方法根据配置来配适对应的StatementHandler对象。它的作用是给3个接口对象的使用提供一个统一且简易的配适器。此为对象的配适,可以使用对象配适器来尽可能地使用已有的类对外提供服务,可以根据需要对外屏蔽或者提供服务,甚至是加入新的朋友。
Executor执行查询时会执行StatementHandler的prepare、parameterize和query方法。
prepare方法
package org.apache.ibatis.executor.statement;
public abstract class BaseStatementHandler implements StatementHandler {
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进行了预编译,然后做一些基础配置,比如超时、获取的最大的行数等的设置。Executor中会调用它的parameterize()方法去设置参数Executor会调用StatementHandler 的parameterize()方法去设置参数
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
显然是调用ParameterHandler去完成的。下面看如何实现查询
执行SQL返回结果
package org.apache.ibatis.executor.statement;
public class PreparedStatementHandler extends BaseStatementHandler {
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
在执行前参数和SQL都被prepare()方法预编译,参数在parameterize()方法中已经进行了设置,所以只要执行SQL,然后返回结果就可以了。执行之后我们看到了ResultSetHandler对结果的封装和返回。
一条查询SQL的执行过程:Executor先调用StatementHandler的prepare()方法预编译SQL,同时设置一些基本运行的参数。然后用parameterize()方法启用ParameterHandler设置参数,完成预编译,执行查询,update()也是如此。如果是查询,MyBatis会使用ResultSetHandler封装结果返回给调用者。
ParameterHandler——参数处理器
MyBatis通过ParameterHandler对预编译语句进行参数设置,作用是完成对预编译参数的设置。
package org.apache.ibatis.executor.parameter;
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
getParameterObject()方法的作用是返回参数对象
setParameters()方法的作用是设置预编译SQL语句的参数。
MyBatis为ParameterHandler提供了一个实现类DefaultParameterHandler,
setParameters的实现
package org.apache.ibatis.scripting.defaults;
public class DefaultParameterHandler implements ParameterHandler {
public void setParameters(PreparedStatement ps) throws SQLException {
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();
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
}
它还是从parameterObject对象中取到参数,然后使用typeHandler转换参数,如果有设置,那么它会根据签名注册的typeHandler对参数进行处理。而typeHandler也是在MyBatis初始化时,注册在Configuration里面的,需要时就可以拿来用了。
ResultSetHandler——结果处理器
ResultSetHandler组装结果返回
ResultSetHandler的接口定义:
package org.apache.ibatis.executor.resultset;
public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
handleOutputParameters()方法是处理存储过程输出 参数的,handleResultSets是包装结果集的。MyBatis提供了一个DefaultResultSetHandler的实现类,在默认情况下都是通过这个类进行处理的。涉及使用Javassist(或者CGLIB)作为延迟加载,然后通过typeHandler和ObjectFactory进行组装结果再返回。改变的几率不高。
SqlSession内部运行
SqlSession是通过执行器Executor调度StatementHandler来运行的。StatementHandler经过3步
prepared预编译SQl
parameterize 设置参数
query/update执行SQL
parameterize调用parameterHandler的方法设置,而 参数是根据类型处理器typeHandler处理的。query/update方法通过ResultSetHandler进行处理结果的封装,如果是update语句,就返回整数,封装就通过typeHandler处理结果类型,然后用ObjectFactory提供的规则组装对象,返回给调用者。