clearcase不能导入项目(万字讲解Executor处理过程)
clearcase不能导入项目(万字讲解Executor处理过程)public class SimpleExecutor extends BaseExecutor { public SimpleExecutor(configuration configuration transaction transaction) { super(configuration transaction); } @Override public int doUpdate(MappedStatement ms Object parameter) throws sqlException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // 创建 StatementHandler 对象 StatementHandle
前面的时候,我整理了一套mybatis的缓存的文章,通过源码,详细的讲解了关于MyBatis一二级缓存的相关知识点,需要的朋友可以看这里
我就不信2W字把源码拆的这么碎,你还不明白mybatis缓存
在讲解缓存的过程中,我提到一个问题
一二级缓存都是在Executor中进行的
但是当时因为篇幅的原因,我只展示BasicExecutor的相关源码操作,其余的几个Executor没有进行完整的介绍,这里和上篇进行衔接,大家可以先去看上一篇,然后再来看这本书,希望对大家能够有所帮助
好啦,话不多说,我们来看一下
SimpleExecutororg.apache.ibatis.executor.SimpleExecutor:继承 BaseExecutor 抽象类,简单的 Executor 实现类(默认使用)
- 每次对数据库的操作,都会创建对应的Statement对象
- 执行完成后,关闭该Statement对象
代码如下:
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(configuration configuration transaction transaction) {
super(configuration transaction);
}
@Override
public int doUpdate(MappedStatement ms Object parameter) throws sqlException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 创建 StatementHandler 对象
StatementHandler handler = configuration.newStatementHandler(this ms parameter RowBounds.DEFAULT null null);
// 初始化 Statement 对象
stmt = prepareStatement(handler ms.getStatementLog());
// 通过 StatementHandler 执行写操作
return handler.update(stmt);
} finally {
// 关闭 Statement 对象
closeStatement(stmt);
}
}
@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 对象
StatementHandler handler = configuration.newStatementHandler(wrapper ms parameter rowBounds resultHandler boundSql);
// 初始化 Statement 对象
stmt = prepareStatement(handler ms.getStatementLog());
// 通过 StatementHandler 执行读操作
return handler.query(stmt resultHandler);
} finally {
// 关闭 Statement 对象
closeStatement(stmt);
}
}
@Override
protected <E> Cursor<E> doQueryCursor(MappedStatement ms Object parameter RowBounds rowBounds BoundSql boundSql)
throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper ms parameter rowBounds null boundSql);
Statement stmt = prepareStatement(handler ms.getStatementLog());
Cursor<E> cursor = handler.queryCursor(stmt);
stmt.closeOnCompletion();
return cursor;
}
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {
return Collections.emptyList();
}
private Statement prepareStatement(StatementHandler handler Log statementLog) throws SQLException {
Statement stmt;
// 获得 Connection 对象,如果开启了 Debug 模式,则返回的是一个代理对象
Connection connection = getConnection(statementLog);
// 创建 Statement 或 PrepareStatement 对象
stmt = handler.prepare(connection transaction.getTimeout());
// 往 Statement 中设置 SQL 语句上的参数,例如 PrepareStatement 的 ? 占位符
handler.parameterize(stmt);
return stmt;
}
}
我们看到这些方法的实现,其中的步骤差不多都是一样的
- 获取Configuration全局配置对象
- 通过上面全局配置对象的newStatementHandler方法,创建RoutingStatementHandler对象,采用了装饰器模式,根据配置的StatementType创建对应的对象,默认为PreparedStatementHandler对象,进入BaseStatementHandler的构造方法你会发现有几个重要的步骤,在后续会讲到然后使用插件链对该对象进行应用,方法如下所示:
// Configuration.java
public StatementHandler newStatementHandler(Executor executor MappedStatement mappedStatement Object parameterObject RowBounds rowBounds ResultHandler resultHandler BoundSql boundSql) {
/* * 创建 RoutingStatementHandler 路由对象
* 其中根据 StatementType 创建对应类型的 Statement 对象,默认为 PREPARED
* 执行的方法都会路由到该对象 */
StatementHandler statementHandler = new RoutingStatementHandler(executor mappedStatement parameterObject rowBounds resultHandler boundSql);
// 将 Configuration 全局配置中的所有插件应用在 StatementHandler 上面
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler; }
- 调用prepareStatement方法初始化Statement对象从事务中获取一个Connection数据库连接,如果开启了Debug模式,则会为该Connection创建一个动态代理对象的实例,用于打印Debug日志通过上面第2步创建的StatementHandler对象创建一个Statement对象(默认为PrepareStatement),还会进行一些准备工作,例如:如果配置了KeyGenerator(设置主键),则会设置需要返回相应自增键,在后续会讲到往Statement对象中设置SQL的参数,例如PrepareStatement的?占位符,实际上是通过DefaultParameterHandler设置占位符参数,
- 通过StatementHandler对Statement进行数据库的操作,如果是查询操作则会通过DefaultResultSetHandler进行参数映射(非常复杂,后续逐步分析)
org.apache.ibatis.executor.ReuseExecutor:继承 BaseExecutor 抽象类,可重用的 Executor 实现类
- 每次对数据库的操作,优先从当前会话的缓存中获取对应的Statement对象,如果不存在,才进行创建,创建好了会放入缓存中
- 数据库操作执行完成后,不关闭该Statement对象
- 其它的和SimpleExecutor是一致的
我们来看看他的prepareStatement方法就好了:
private Statement prepareStatement(StatementHandler handler Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
/*
* 根据需要执行的 SQL 语句判断 是否已有对应的 Statement 并且连接未关闭
*/
if (hasStatementFor(sql)) {
// 从缓存中获得 Statement 对象
stmt = getStatement(sql);
// 重新设置事务超时时间
applyTransactionTimeout(stmt);
} else {
// 获得 Connection 对象
Connection connection = getConnection(statementLog);
// 初始化 Statement 对象
stmt = handler.prepare(connection transaction.getTimeout());
// 将 Statement 添加到缓存中,key 值为 当前执行的 SQL 语句
putStatement(sql stmt);
}
// 往 Statement 中设置 SQL 语句上的参数,例如 PrepareStatement 的 ? 占位符
handler.parameterize(stmt);
return stmt;
}
在创建Statement对象前,会根据本次查询的SQL从本地的Map<String Statement> statementMap获取到对应的Statement对象
- 如果缓存命中,并且该对象的连接未关闭,那么重新设置当前事务的超时时间
- 如果缓存未命中,则执行和SimpleExecutor中的prepareStatement方法相同逻辑创建一个Statement对象并放入statementMap缓存中
org.apache.ibatis.executor.BatchExecutor:继承 BaseExecutor 抽象类,支持批量执行的 Executor 实现类
- 我们在执行数据库的更新操作时,可以通过Statement的addBatch()方法将数据库操作添加到批处理中,等待调用Statement的executeBatch()方法进行批处理
- BatchExecutor维护了多个Statement对象,一个对象对应一个SQL(sql和MappedStatement对象都相等),每个Statement对象对应多个数据库操作(同一个sql多种入参),就像苹果蓝里装了很多苹果,番茄蓝里装了很多番茄,最后,再统一倒进仓库
由于JDBC不支持数据库查询的批处理,所以这里就不展示它数据库查询的实现方法,和SimpleExecutor一致,我们来看看其他的方法
构造方法public class BatchExecutor extends BaseExecutor {
public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE 1002;
/**
* Statement 数组
*/
private final List<Statement> statementList = new ArrayList<>();
/**
* BatchResult 数组
*
* 每一个 BatchResult 元素,对应 {@link #statementList} 集合中的一个 Statement 元素
*/
private final List<BatchResult> batchResultList = new ArrayList<>();
/**
* 上一次添加至批处理的 Statement 对象对应的SQL
*/
private String currentSql;
/**
* 上一次添加至批处理的 Statement 对象对应的 MappedStatement 对象
*/
private MappedStatement currentStatement;
public BatchExecutor(Configuration configuration Transaction transaction) {
super(configuration transaction);
}
}
- statementList属性:维护多个Statement对象
- batchResultList属性:维护多个BatchResult对象,每个对象对应上面的一个Statement对象,每个BatchResult对象包含同一个SQL和其每一次操作的入参
- currentSql属性:上一次添加至批处理的Statement对象对应的SQL
- currentStatement属性:上一次添加至批处理的Statement对象对应的MappedStatement对象
org.apache.ibatis.executor.BatchResult:相同SQL(sql和MappedStatement对象都相等)聚合的结果,包含了同一个SQL每一次操作的入参,代码如下:
public class BatchResult {
/**
* MappedStatement 对象
*/
private final MappedStatement mappedStatement;
/**
* SQL
*/
private final String sql;
/**
* 参数对象集合
*
* 每一个元素,对应一次操作的参数
*/
private final List<Object> parameterObjects;
/**
* 更新数量集合
*
* 每一个元素,对应一次操作的更新数量
*/
private int[] updateCounts;
public BatchResult(MappedStatement mappedStatement String sql) {
super();
this.mappedStatement = mappedStatement;
this.sql = sql;
this.parameterObjects = new ArrayList<>();
}
public BatchResult(MappedStatement mappedStatement String sql Object parameterObject) {
this(mappedStatement sql);
addParameterObject(parameterObject);
}
public void addParameterObject(Object parameterObject) {
this.parameterObjects.add(parameterObject);
}
}
doUpdate方法
更新数据库的操作,添加至批处理,需要调用doFlushStatements执行批处理,代码如下:
@Override
public int doUpdate(MappedStatement ms Object parameterObject) throws SQLException {
final Configuration configuration = ms.getConfiguration();
// <1> 创建 StatementHandler 对象
final StatementHandler handler = configuration.newStatementHandler(this ms parameterObject RowBounds.DEFAULT null null);
final BoundSql boundSql = handler.getBoundSql();
final String sql = boundSql.getSql();
final Statement stmt;
// <2> 如果和上一次添加至批处理 Statement 对象对应的 currentSql 和 currentStatement 都一致,则聚合到 BatchResult 中
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
// <2.1> 获取上一次添加至批处理 Statement 对象
int last = statementList.size() - 1;
stmt = statementList.get(last);
// <2.2> 重新设置事务超时时间
applyTransactionTimeout(stmt);
// <2.3> 往 Statement 中设置 SQL 语句上的参数,例如 PrepareStatement 的 ? 占位符
handler.parameterize(stmt);// fix Issues 322
// <2.4> 获取上一次添加至批处理 Statement 对应的 BatchResult 对象,将本次的入参添加到其中
BatchResult batchResult = batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else { // <3> 否则,创建 Statement 和 BatchResult 对象
// <3.1> 初始化 Statement 对象
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection transaction.getTimeout());
handler.parameterize(stmt); // fix Issues 322
// <3.2> 设置 currentSql 和 currentStatemen
currentSql = sql;
currentStatement = ms;
// <3.3> 添加 Statement 到 statementList 中
statementList.add(stmt);
// <3.4> 创建 BatchResult 对象,并添加到 batchResultList 中
batchResultList.add(new BatchResult(ms sql parameterObject));
}
// <4> 添加至批处理
handler.batch(stmt);
// <5> 返回 Integer.MIN_VALUE 1002
return BATCH_UPDATE_RETURN_VALUE;
}
- 创建StatementHandler对象,和SimpleExecutor中一致,在后续会讲到
- 如果和上一次添加至批处理Statement对象对应的currentSql和currentStatement都一致,则聚合到BatchResult中获取上一次添加至批处理Statement对象重新设置事务超时时间往Statement中设置 SQL 语句上的参数,例如PrepareStatement的?占位符,在SimpleExecutor中已经讲到获取上一次添加至批处理Statement对应的BatchResult对象,将本次的入参添加到其中
- 否则,创建Statement和BatchResult对象初始化Statement对象,在SimpleExecutor中已经讲到,这里就不再重复了设置currentSql和currentStatemen属性添加Statement到statementList集合中创建BatchResult对象,并添加到batchResultList集合中
- 添加至批处理
- 返回Integer.MIN_VALUE 1002,为什么返回这个值?不清楚
执行批处理,也就是将之前添加至批处理的数据库更新操作进行批处理,代码如下:
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
try {
List<BatchResult> results = new ArrayList<>();
if (isRollback) { // <1> 如果 isRollback 为 true ,返回空数组
return Collections.emptyList();
}
// <2> 遍历 statementList 和 batchResultList 数组,逐个提交批处理
for (int i = 0 n = statementList.size(); i < n; i ) {
// <2.1> 获得 Statement 和 BatchResult 对象
Statement stmt = statementList.get(i);
applyTransactionTimeout(stmt);
BatchResult batchResult = batchResultList.get(i);
try {
// <2.2> 提交该 Statement 的批处理
batchResult.setUpdateCounts(stmt.executeBatch());
MappedStatement ms = batchResult.getMappedStatement();
List<Object> parameterObjects = batchResult.getParameterObjects();
/*
* <2.3> 获得 KeyGenerator 对象
* 1. 配置了 <selectKey /> 则会生成 SelectKeyGenerator 对象
* 2. 配置了 useGeneratedKeys="true" 则会生成 Jdbc3KeyGenerator 对象
* 否则为 NoKeyGenerator 对象
*/
KeyGenerator keyGenerator = ms.getKeyGenerator();
if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
// <2.3.1> 批处理入参对象集合,设置自增键
jdbc3KeyGenerator.processBatch(ms stmt parameterObjects);
} else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { // issue #141
for (Object parameter : parameterObjects) {
// <2.3.1> 一次处理每个入参对象,设置自增键
keyGenerator.processAfter(this ms stmt parameter);
}
}
// Close statement to close cursor #1109
// <2.4> 关闭 Statement 对象
closeStatement(stmt);
} catch (BatchUpdateException e) {
// 如果发生异常,则抛出 BatchExecutorException 异常
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);
}
// <2.5> 添加到结果集
results.add(batchResult);
}
return results;
} finally {
// <3.1> 关闭 Statement 们
for (Statement stmt : statementList) {
closeStatement(stmt);
}
// <3.2> 置空 currentSql、statementList、batchResultList 属性
currentSql = null;
statementList.clear();
batchResultList.clear();
}
}
在调用doUpdate方法将数据库更新操作添加至批处理后,我们需要调用doFlushStatements方法执行批处理,逻辑如下:
- 如果isRollback为true,表示需要回退,返回空数组
- 遍历statementList和batchResultList数组,逐个提交批处理获得Statement和BatchResult对象提交该Statement的批处理获得KeyGenerator对象,用于设置自增键,在后续会讲到关闭Statement对象将BatchResult对象添加到结果集
- 最后会关闭所有的Statement和清空当前会话中保存的数据
前后两篇文章,希望大家能够一起查看一下,在大家的学习路上能够贡献一份力量