上一篇中介绍了SqlSession如何去获得Mapper代理对象并且执行Sql语句的过程。并且提到SqlSession中的增删改查方法也都是通过Executor来实现的,这一篇也是讲解一下Executor的具体实现
这是Executor的继承结构
首先看顶层接口Executor
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
/**
* @author Clinton Begin
*/
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
/**
* 负责处理新增,修改和删除操作
* @param ms seeMappedStatement 对象 作用:对操作数据库存储封装,包括 sql 语句、输入输出参数
* @see MappedStatement
* @param parameter 请求参数。修改新增操作时的vo信息
* */
int update(MappedStatement ms, Object parameter) throws SQLException;
/**
* @param ms seeMappedStatement 对象 作用:对操作数据库存储封装,包括 sql 语句、输入输出参数
* @param parameter 请求参数 查询时条件信息
* @param rowBounds 逻辑分页参数
* @param resultHandler 处理返回结果
* @param cacheKey 缓存key
* @param boundSql sql信息
* */
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
/**
* 游标查询 返回一个可以迭代的对象 可以进行迭代获取。避免数据量大占用大量内存
*
* */
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
/**
* 提交事务
* */
void commit(boolean required) throws SQLException;
/**
* 回滚事务
* */
void rollback(boolean required) throws SQLException;
/**
* 创建缓存的键对象
* */
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
/**
* 缓存中是否有这个查询的结果
* */
boolean isCached(MappedStatement ms, CacheKey key);
/**
* 清空缓存
* */
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
BaseExecutor
BaseExecutor作为Executor的抽象实现类。并不是直接去进行操作的。而是交给其下面三个子类执行器SimpleExecutor,ReuseExecutor,BatchExecutor来进行操作。BaseExecutor实现Executor里面的所有方法。把部分公共的实现给做好。在吧调用不同执行器的代码提取成抽象方法 给子类进行实现。这里运用到的设计模式是模版模式。
update
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 数据变更操作会清空一级缓存
clearLocalCache();
return doUpdate(ms, parameter);
}
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
这里由三个执行器来实现doUpdate方法
query
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 这里其实就是拿sql语句,BoundSql是对动态SQL解析生成的SQL语句和参数映射信息的封装
BoundSql boundSql = ms.getBoundSql(parameter);
// 创建CacheKey用于缓存【这里面有很多因素,用来决定key是否相同】
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 具体执行查询的地方
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
创建缓存
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
// mapper的唯一id丢进去 命名空间+方法名
cacheKey.update(ms.getId());
// offset丢进去【查询结果的偏移量】
cacheKey.update(rowBounds.getOffset());
// limit丢进去【查询的条数】
cacheKey.update(rowBounds.getLimit());
// 完整的SQL语句丢进去
cacheKey.update(boundSql.getSql());
// 拿到入参映射
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// 拿到类型处理器注册中心 顾名思义处理类型
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
// 遍历入参映射
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 入参属性名 有的有指定@Param的嘛
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
// 入参为空 值也为空
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 从类型处理器注册中心 拿到与入参实际类型匹配的类型处理器
value = parameterObject;
} else {
// 拿到一个metaObject对象
MetaObject metaObject = configuration.newMetaObject(parameterObject);
// 拿出里面的propertyName的值
value = metaObject.getValue(propertyName);
}
// 再把入参值 丢进缓存
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
// 判断有没有环境属性,有的话把环境id也丢进缓存里
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
最后调用的查询
@SuppressWarnings("unchecked")
@Override
public <E> List<E> 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.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
// 查一下本地缓存(一级缓存)是否有值
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 走缓存
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 走数据库
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
不走缓存时的调用方法doQuery
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> 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;
}
Executo所定义的抽象方法都由不同的子类执行器来实现
// 定义的四个抽象方法,在去掉 do 前缀的相应方法中被调用
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
接下来看一下Executor的子类实现
SimpleExecutor
简单执行器。根据要求执行对应的sql。拼接好sql后直接交给StatementHandler 去执行。每次查询完成之后都会释放Statement
/**
* Copyright 2009-2019 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;
/**
* @author Clinton Begin
*/
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 handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
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 configuration = ms.getConfiguration();
// 拿到一个StatementHandler对象,具体是RoutingStatementHandler类型,里面还注册了插件
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 解析成一个prepareStatement对象,并设置参数等
stmt = prepareStatement(handler, ms.getStatementLog());
// 用StatementHandler来处理查询,默认是PreparedStatementHandler
return handler.query(stmt, resultHandler);
} finally {
// 释放资源
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;
// 获取JDBC连接对象
Connection connection = getConnection(statementLog);
// 创建Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 继续调用StatementHandler的方法设置参数
handler.parameterize(stmt);
return stmt;
}
}
测试
/**
* 简单执行器
* 每次都会创建一个新的处理器(PrepareStatement)
* */
@Test
public void SimpleExecutorTest() throws SQLException {
SimpleExecutor executor = new SimpleExecutor(configuration, jdbcTransaction);
MappedStatement queryMs = configuration.getMappedStatement("com.example.smallwhite.mybatis.dao.BabyimageDao.queryById");
//参数 1.执行的MappedStatement 2.参数信息 3.分页信息
executor.doQuery(queryMs, "0Q55mn3P9PcbAMa73V361", RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER, queryMs.getBoundSql("0Q55mn3P9PcbAMa73V361"));
executor.doQuery(queryMs, "0Q55mn3P9PcbAMa73V361", RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER, queryMs.getBoundSql("0Q55mn3P9PcbAMa73V361"));
MappedStatement updateMs = configuration.getMappedStatement("com.example.smallwhite.mybatis.dao.BabyimageDao.update");
//参数 1.执行的MappedStatement 2.参数信息 3.分页信息
executor.doUpdate(updateMs, new Babyimage("0Q55mn3P9PcbAMa73V361", "q", "q", new Timestamp(System.currentTimeMillis()),"11"));
executor.doUpdate(updateMs, new Babyimage("0Q55mn3P9PcbAMa73V361", "q", "q", new Timestamp(System.currentTimeMillis()),"11"));
}
结果
==> Preparing: select id, name, url, ts from babyimage where id = ?
==> Parameters: 0Q55mn3P9PcbAMa73V361(String)
<== Columns: id, name, url, ts
<== Row: 0Q55mn3P9PcbAMa73V361, q, q, 2021-08-17 09:56:19.228000
<== Total: 1
==> Preparing: select id, name, url, ts from babyimage where id = ?
==> Parameters: 0Q55mn3P9PcbAMa73V361(String)
<== Columns: id, name, url, ts
<== Row: 0Q55mn3P9PcbAMa73V361, q, q, 2021-08-17 09:56:19.228000
<== Total: 1
==> Preparing: update babyimage SET name = ?, url = ?, ts = ? where id = ?
==> Parameters: q(String), q(String), 2021-08-17 20:03:21.605(Timestamp), 0Q55mn3P9PcbAMa73V361(String)
<== Updates: 1
==> Preparing: update babyimage SET name = ?, url = ?, ts = ? where id = ?
==> Parameters: q(String), q(String), 2021-08-17 20:03:21.633(Timestamp), 0Q55mn3P9PcbAMa73V361(String)
<== Updates: 1
ReuseExecutor
可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该session中有效,所以使用完成后记得调用
flushStatements
来清除Map。调用实现的四个抽象方法时会调用 prepareStatement()
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
/**
* @author Clinton Begin
*/
public class ReuseExecutor extends BaseExecutor {
private final Map<String, Statement> statementMap = new HashMap<>();
public ReuseExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
@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);
}
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}
@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());
return handler.queryCursor(stmt);
}
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {
//逐一关闭连接 并且清空statementMap
for (Statement stmt : statementMap.values()) {
closeStatement(stmt);
}
statementMap.clear();
return Collections.emptyList();
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//在statementMap获取 sql相同的对象 拿到Statement来操作sql 节省开销
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
//不存在得话重新创建 并存储到statementMap中
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
private boolean hasStatementFor(String sql) {
try {
Statement statement = statementMap.get(sql);
return statement != null && !statement.getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
private Statement getStatement(String s) {
return statementMap.get(s);
}
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}
}
测试
/**
* 可重用执行器
* 相同的sql只进行一次预处理
* */
@Test
public void ReuseExecutorTest() throws SQLException {
ReuseExecutor executor = new ReuseExecutor(configuration, jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.example.smallwhite.mybatis.dao.BabyimageDao.queryById");
//参数 1.执行的MappedStatement 2.参数信息 3.分页信息
List<Object> objects = executor.doQuery(ms, "0Q55mn3P9PcbAMa73V361", RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER, ms.getBoundSql("0Q55mn3P9PcbAMa73V361"));
executor.doQuery(ms, "0Q55mn3P9PcbAMa73V361", RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER, ms.getBoundSql("0Q55mn3P9PcbAMa73V361"));
System.out.println(objects.get(0));
}
结果
==> Preparing: select id, name, url, ts from babyimage where id = ?
==> Parameters: 0Q55mn3P9PcbAMa73V361(String)
<== Columns: id, name, url, ts
<== Row: 0Q55mn3P9PcbAMa73V361, q, q, 2021-08-17 12:03:21.633000
<== Total: 1
==> Parameters: 0Q55mn3P9PcbAMa73V361(String)
<== Columns: id, name, url, ts
<== Row: 0Q55mn3P9PcbAMa73V361, q, q, 2021-08-17 12:03:21.633000
<== Total: 1
==> Preparing: update babyimage SET name = ?, url = ?, ts = ? where id = ?
==> Parameters: q(String), q(String), 2021-08-17 20:05:25.677(Timestamp), 0Q55mn3P9PcbAMa73V361(String)
<== Updates: 1
==> Parameters: q(String), q(String), 2021-08-17 20:05:25.706(Timestamp), 0Q55mn3P9PcbAMa73V361(String)
<== Updates: 1
BatchExecutor
通过批量操作来优化性能。通常需要注意的是批量更新操作,不会自动提交事务。需要手动commit或者调用doFlushStatements。由于内部有缓存的实现,使用完成后记得调用
flushStatements
来清除缓存。
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
/**
* @author Jeff Butler
*/
public class BatchExecutor extends BaseExecutor {
public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;
private final List<Statement> statementList = new ArrayList<>();
private final List<BatchResult> batchResultList = new ArrayList<>();
private String currentSql;
private MappedStatement currentStatement;
public BatchExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
final Configuration configuration = ms.getConfiguration();
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;
//判断 sql 和 configuration是否一致
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
stmt = statementList.get(last);
applyTransactionTimeout(stmt);
handler.parameterize(stmt);// fix Issues 322
BatchResult batchResult = batchResultList.get(last);
//一致的话把参数追加到parameterObjects后面进行批量操作
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); // fix Issues 322
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
//创建第一条批量执行 信息 包括sql 参数信息
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
//将SQL命令添加到批处理执行列表中 但是并不会执行 直到调用commit或者 doFlushStatements方法后执行数据库操作
handler.batch(stmt);
return BATCH_UPDATE_RETURN_VALUE;
}
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
@Override
protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
Connection connection = getConnection(ms.getStatementLog());
Statement stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
Cursor<E> cursor = handler.queryCursor(stmt);
stmt.closeOnCompletion();
return cursor;
}
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
try {
List<BatchResult> results = new ArrayList<>();
if (isRollback) {
return Collections.emptyList();
}
for (int i = 0, n = statementList.size(); i < n; i++) {
Statement stmt = statementList.get(i);
applyTransactionTimeout(stmt);
BatchResult batchResult = batchResultList.get(i);
try {
batchResult.setUpdateCounts(stmt.executeBatch());
MappedStatement ms = batchResult.getMappedStatement();
List<Object> parameterObjects = batchResult.getParameterObjects();
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
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) {
closeStatement(stmt);
}
currentSql = null;
statementList.clear();
batchResultList.clear();
}
}
}
测试
/**
* 批处理执行器
* 只针对新增,删除,修改操作
* */
@Test
public void BatchExecutorTest() throws SQLException {
BatchExecutor executor = new BatchExecutor(configuration, jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.example.smallwhite.mybatis.dao.BabyimageDao.update");
//参数 1.执行的MappedStatement 2.参数信息 3.分页信息
int i = executor.doUpdate(ms, new Babyimage("0Q55mn3P9PcbAMa73V361", "q", "q", new Timestamp(System.currentTimeMillis()),"11"));
executor.doUpdate(ms, new Babyimage("0Q55mn3P9PcbAMa73V361", "q", "q", new Timestamp(System.currentTimeMillis()),"11"));
//通过doFlushStatements 或者 commit来提交事务
// executor.doFlushStatements(false);
executor.commit(false);
executor.doUpdate(ms, new Babyimage("0Q55mn3P9PcbAMa73V361", "q", "q", new Timestamp(System.currentTimeMillis()),"11"));
System.out.println(i);
}
结果
==> Preparing: update babyimage SET name = ?, url = ?, ts = ? where id = ?
==> Parameters: q(String), q(String), 2021-08-17 20:34:20.352(Timestamp), 0Q55mn3P9PcbAMa73V361(String)
==> Parameters: q(String), q(String), 2021-08-17 20:34:20.411(Timestamp), 0Q55mn3P9PcbAMa73V361(String)
==> Preparing: update babyimage SET name = ?, url = ?, ts = ? where id = ?
==> Parameters: q(String), q(String), 2021-08-17 20:34:20.415(Timestamp), 0Q55mn3P9PcbAMa73V361(String)
-2147482646
CachingExecutor
CachingExecutor是二级缓存执行器。通过装饰器模式加载到执行器中。可以在xml中配置启用。Executor执行时会先调用CachingExecutor查找缓存中是否存在。不存在就去数据库查找
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 拿到查询语句 其实简单理解就是mapper.xml或者注解里的sql解析为占位符形式
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 创建缓存的key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
// 执行查询
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 拿MappedStatement维护的二级缓存对象
Cache cache = ms.getCache();
if (cache != null) {
// 判断是否需要刷新二级缓存
// 判断:如果有缓存,且<select|update|delete|insert>标签中的flushCache属性开启了,就清空缓存
// 因为<select>标签的flushCache默认是false,所以不会清理
// 而<update|delete|insert>标签的flushCache默认是true,所以会清理缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
// 从MappedStatement对象对应的二级缓存中获取数据
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// 数据不存在则调用装饰查询
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 将数据丢入到MappedStatement维护的二级缓存对象中去
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 拿不到缓存数据 就进行查询
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}