ReuseExecutor
2020-09-10 本文已影响0人
93张先生
ReuseExecutor
在传统的 JDBC 编程中,重用 Statement 对象是常用的一种优化手段,该优化手段可以减少 SQL 预编译的开销以及创建和销毁 Statement 对象的开销,从而提高性能。ReuseExecutor 提供了 Statement 重用功能,ReuseExecutor 中通过 statementMap 字段 缓存使用过的 Statement 对象,key 是 SQL 语句,value 是 SQL 对应的 Statement 对象。
ReuseExecutor.doQuery()、 doQueryCursor()、 doUpdate() 方法的实现与 SimpleExecutor 对应方法的实现一样,区别在于其中调用的 prepareStatement() 方法, SimpleExecutor 每次都会通过 JDBC Connection 创建新的 Statement 对象,而 ReuseExecutor 会先尝试重用 StatementMap 中缓存的 Statement 对象。
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);
}
// TODO: 2020/9/3 游标缓存,相同 SQL 多次查询,会导致前一个查询的结果集 DefaultCursor 被关闭,344 page
@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);
}
// TODO: 2020/9/3 提交或回滚
/**
*
* 当事务提交或回滚、连接关闭时,都需要关闭这些缓存的 Statement 对象。前面介绍
* BaseExecutor.commit()、 rollback() 和 close()方法时提到,其中都会调用 doFlushStatements()方法,所以在该方法 实现关 Statement 对象的逻辑非常合适。
* @param isRollback
* @return
*/
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {
// 边历 statementMap 合并关闭其中的 Statement 对象
for (Statement stmt : statementMap.values()) {
// 关闭 Statement 对象
closeStatement(stmt);
}
// 清空缓存
statementMap.clear();
// 返回空集合
return Collections.emptyList();
}
/**
* 通过 statementMap 缓存获取 Statement,或者创建,或者从缓存中获取
* @param handler
* @param statementLog
* @return
* @throws SQLException
*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql(); // 可以执行的包含 "?" 占位符的 sql
// 缓存中是否存在 Statement 对象
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
// 修改超时时间
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
// 创建 Statement 对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 放入缓存
putStatement(sql, stmt);
}
// 处理占位符
handler.parameterize(stmt);
return stmt;
}
private boolean hasStatementFor(String sql) {
try {
return statementMap.keySet().contains(sql) && !statementMap.get(sql).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);
}
}