讲到Mybaits就不得不提它的执行器Executor,我们知道Mybaits是ORM框架(关系映射),那么它的mapper语句就是通过Executor执行的。
Executor是一个顶层接口,它又分为两大实现类:BaseExecutor、CachingExecutor;
BaseExecutor又分为三大子类SimpleExecutor、BatchExecutor、ReuseExecutor;
Mybaits的Executor可以在xml配置文件中进行配置,默认使用的SimpleExecutor
<settings>
<!--SIMPLE、REUSE、BATCH-->
<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>
1、Executor
(顶层接口、父接口)
2.BaseExecutor
(抽象类,实现了Executor接口,实现了执行器的基本功能,采用模板方法的设计模式)
源码有点多,看部分重要的源码
2.1先来看属性和构造
PerpetualCache
2.2.事务相关处理
2.3. update方法
2.4.doUpdate
2.4.query
2.5.createCacheKey
2.6.抽象方法用于给子类去实现
获取数据库连接
2.7.Configuration
BaseExecutor的实现类是在Configuration中创建出来的
3.SimpleExecutor
(默认的执行器,最简单的执行器,根据对应的sql直接执行即可,不会做一些额外的操作;拼接完SQL之后,直接交给 StatementHandler去执行。每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)
3.1doUpdate
3.2doQuery
3.3prepareStatement
3.4.prepare
3.5parameterize
CallableStatementHandler中的parameterize
ParameterHandler中的parameterize
SimpleStatementHandler中的parameterize
4.BatchExecutor(重点、难点)
执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;可以是Statement或PrepareStatement对象.
4.1doUpdate
// 缓存多个Statement对象,每个Statement都是addBatch()后,等待执行
private final List<Statement> statementList = new ArrayList<Statement>();
// 对应的结果集(主要保存了update结果的count数量)
private final List<BatchResult> batchResultList = new ArrayList<BatchResult>();
// 当前保存的sql,即上次执行的sql
private String currentSql;
/**
* 更新
*/
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
//获取Configuration对象,获得配置信息
final Configuration configuration = ms.getConfiguration();
//获取StatementHandler对象
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
//获取BoundSql对象
final BoundSql boundSql = handler.getBoundSql();
//获得Sql语句
final String sql = boundSql.getSql();
final Statement stmt;
/**
* 如果sql等于currentSql同时MappedStatement与currentStatement相同, 就是同一条SQL,但是参数可能不同,这样就不需要重复创建PrepareStatement
*
* 可以减少网络交互次次数,通过源码可以发现批处理中最佳时间就是同样的sql要一起执行,不要存在不同sql间隔这样的场景出现
*/
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
//获取最后一次创建statement
stmt = statementList.get(last);
//设置事务超时时间
applyTransactionTimeout(stmt);
handler.parameterize(stmt);//fix Issues 322
//获取对应的批量结果
BatchResult batchResult = batchResultList.get(last);
//将参数对象添加到参数列表中
batchResult.addParameterObject(parameterObject);
} else {//和上一次创建的SQL不同,则需要重新创建PrepareStatement
//获取数据库连接
Connection connection = getConnection(ms.getStatementLog());
//创建一个Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); //fix Issues 322
currentSql = sql;
currentStatement = ms;
//添加批量处理操作
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
// handler.parameterize(stmt);
//添加到批处理,最终是调用jdbc的批处理操作
handler.batch(stmt);
//返回默认值
return BATCH_UPDATE_RETURN_VALUE;
}
4.3.doQuery
/**
* 查询
*/
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
//刷新Statements,默认是设置为false,不进行事务回滚
flushStatements();
//获取配置信息
Configuration configuration = ms.getConfiguration();
//获取Statement处理器对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject,
rowBounds, resultHandler, boundSql);
//获取数据库连接
Connection connection = getConnection(ms.getStatementLog());
//获取Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return handler.<E>query(stmt, resultHandler);
} finally {
//关闭Statement
closeStatement(stmt);
}
}
4.4. doFlushStatements
/**
* 所调用的flushStatements其实就是调用了doFlushStatements
*/
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
try {
List<BatchResult> results = new ArrayList<BatchResult>();
//默认isRollback为false,如果true,进行了事务回滚返回空列表
if (isRollback) {
return Collections.emptyList();
}
for (int i = 0, n = statementList.size(); i < n; i++) {//遍历所有satement
Statement stmt = statementList.get(i);
applyTransactionTimeout(stmt);
//获取对应的结果对象
BatchResult batchResult = batchResultList.get(i);
try {
//stmt.executeBatch执行批处理,并将更新条数保存到执行结果中;
batchResult.setUpdateCounts(stmt.executeBatch());
//获取结果对应到mappedStatement
MappedStatement ms = batchResult.getMappedStatement();
//获取参数列表
List<Object> parameterObjects = batchResult.getParameterObjects();
/**
* KeyGenerator用于生成数据库主键或将主键重置到pojo中
*
* KeyGenerator接口定义了2个函数:
*
* //执行insert之前
* 1. void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
*
* //insert之后
* 2. void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
*
* NoKeyGenerator:没有执行任何操作,并且是Mybatis的默认实现
*
* Jdbc3KeyGenerator:适用于可以自动生成主键的sql(自增等),由于Statement.execute执行后返回的是操作行数,并不会返回主键,当配置了
* <insert id="insert" useGeneratedKeys="true" keyProperty="id">
* 后,该函数会自动将id赋值到keyProperty对应的javabean属性中
*
* SelectKeyGenerator:通过自定义sql手动获取主键值,有2种配置,before和after
* before既是在insert之前设置到pojo中作为参数一起insert到db
* after即为在insert之后,通过自定义sql获取并设置到pojo中
*
* <selectKey resultType="java.lang.Integer" keyProperty="id" order="BEFORE">
* select max(id) from TB_USER
* </selectKey>
*
*/
KeyGenerator keyGenerator = ms.getKeyGenerator();
if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
} else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141
for (Object parameter : parameterObjects) {
keyGenerator.processAfter(this, ms, stmt, parameter);
}
}
// Close statement to close cursor #1109
//关闭Staement
closeStatement(stmt);
} catch (BatchUpdateException e) {
StringBuilder message = new StringBuilder();
message.append(batchResult.getMappedStatement().getId())
.append(" (batch index #")
.append(i + 1)
.append(")")
.append(" failed.");
if (i > 0) {
message.append(" ")
.append(i)
.append(" prior sub executor(s) completed successfully, but will be rolled back.");
}
throw new BatchExecutorException(message.toString(), e, results, batchResult);
}
results.add(batchResult);
}
return results;
} finally {
for (Statement stmt : statementList) {//遍历一个个的关闭Statement
closeStatement(stmt);
}
currentSql = null;
statementList.clear();
batchResultList.clear();
}
}
5.ReuseExecutor
(可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该session中有效,所以使用完成后记得调用flushStatements来清除Map。)
***5.1.doUpdate ***
/**
* 更新
*/
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
//获取配置信息
Configuration configuration = ms.getConfiguration();
//从配置信息里创建一个新的StatementHandler处理器对象
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//获取Statement对象
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//处理器执行更新
return handler.update(stmt);
}
5.2.ReuseExecutor
/**
* 查询
*/
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
//获取配置信息
Configuration configuration = ms.getConfiguration();
//从配置信息里创建一个新的StatementHandler处理器对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//获取Statement对象
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//处理器执行查询
return handler.<E>query(stmt, resultHandler);
}
doFlushStatements
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
for (Statement stmt : statementMap.values()) {//遍历statementMap 关闭Statement
closeStatement(stmt);
}
statementMap.clear();//清除statementMap
//返回空列表
return Collections.emptyList();
}
prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {//如果缓存了该SQL,则返回其Statement对象
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {// 如果没有缓存该SQL,则创建SQL的Statement,并加入缓存
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
/**
* 是否缓存了sql
* @param sql
* @return
*/
private boolean hasStatementFor(String sql) {
try {
return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
/**
* 返回指定sql的 Statement
* @param s
* @return
*/
private Statement getStatement(String s) {
return statementMap.get(s);
}
/**
* 添加SQL和Statement
* @param sql
* @param stmt
*/
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}