Mybatis BatchExecutor源码分析
LD is tigger forever,CG are not brothers forever, throw the pot and shine forever.
Modesty is not false, solid is not naive, treacherous but not deceitful, stay with good people, and stay away from poor people.
talk is cheap, show others the code,Keep progress,make a better result.
Survive during the day and develop at night。
目录
概 述
Mybatis BatchExecutor源码分析
//加载驱动
Class.forName("org.hsqldb.jdbcDriver");
//获取连接
Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:batch_keys", "sa", "");
//设置不自动提交
connection.setAutoCommit(false);
//创建prepareStatement,存在一次网络交互,预存储statement
PreparedStatement preparedStatement=connection.prepareStatement("insert into users values(?,?) ");
//设置参数
preparedStatement.setInt(1, 1);
preparedStatement.setString(2, "Cujo");
//执行sql 网络交互一次
preparedStatement.executeUpdate();
preparedStatement.setInt(1, 2);
preparedStatement.setString(2, "zhangsan");
preparedStatement.addBatch();
//执行sql 网络交互一次
preparedStatement.executeUpdate();
为了减少网络的次数的使用批处理的方式实现,代码如下:
//加载驱动
Class.forName("org.hsqldb.jdbcDriver");
//获取连接
Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:batch_keys", "sa", "");
//设置不自动提交
connection.setAutoCommit(false);
//创建prepareStatement,存在一次网络交互,预存储statement
PreparedStatement preparedStatement=connection.prepareStatement("insert into users values(?,?) ");
//设置参数
preparedStatement.setInt(1, 1);
preparedStatement.setString(2, "Cujo");
//添加到批处理
preparedStatement.addBatch();
preparedStatement.setInt(1, 2);
preparedStatement.setString(2, "zhangsan");
preparedStatement.addBatch();
//执行批处理,该处将添加到批处理中的sql都执行完
preparedStatement.executeBatch();
主要的处理过程:
1.首先指定ExecutorType.BATCH打开sqlSession
2.多长重复执行单挑SQL
3.执行结束后调用flushStatements()执行批处理
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
//获取配置信息
final Configuration configuration = ms.getConfiguration();
//创建StatementHandler
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
//获取BoundSql
final BoundSql boundSql = handler.getBoundSql();
//从boundSql中获取 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);
//设置stmt参数
handler.parameterize(stmt);
//获取对应的批量结果
BatchResult batchResult = batchResultList.get(last);
//将参数对象添加到参数列表中
batchResult.addParameterObject(parameterObject);
} else {//和上一次创建的SQL不同,则需要重新创建PrepareStatement
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
//添加到批处理
handler.batch(stmt);
//返回默认值
return BATCH_UPDATE_RETURN_VALUE;
}
@Override
public List doFlushStatements(boolean isRollback) throws SQLException {
try {
List results = new ArrayList();
if (isRollback) {
return Collections.emptyList();
}
//遍历所有satement
for (int i = 0, n = statementList.size(); i < n; i++) {
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
@Override
public List doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
//刷新
flushStatements();
//获取配置
Configuration configuration = ms.getConfiguration();
//创建StatementHandler,该处可以通过插件增强
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
//获取连接
Connection connection = getConnection(ms.getStatementLog());
//预处理
stmt = handler.prepare(connection, transaction.getTimeout());
//设置参数
handler.parameterize(stmt);
//执行了SQL 也就是说查询是没有批处理的
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
//获取配置信息
final Configuration configuration = ms.getConfiguration();
//创建StatementHandler
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
//获取BoundSql
final BoundSql boundSql = handler.getBoundSql();
//从boundSql中获取 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);
//设置stmt参数
handler.parameterize(stmt);
//获取对应的批量结果
BatchResult batchResult = batchResultList.get(last);
//将参数对象添加到参数列表中
batchResult.addParameterObject(parameterObject);
} else {//和上一次创建的SQL不同,则需要重新创建PrepareStatement
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
//添加到批处理
handler.batch(stmt);
//返回默认值
return BATCH_UPDATE_RETURN_VALUE;
}
@Override
public List doFlushStatements(boolean isRollback) throws SQLException {
try {
List results = new ArrayList();
if (isRollback) {
return Collections.emptyList();
}
//遍历所有satement
for (int i = 0, n = statementList.size(); i < n; i++) {
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
@Override
public List doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
//刷新
flushStatements();
//获取配置
Configuration configuration = ms.getConfiguration();
//创建StatementHandler,该处可以通过插件增强
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
//获取连接
Connection connection = getConnection(ms.getStatementLog());
//预处理
stmt = handler.prepare(connection, transaction.getTimeout());
//设置参数
handler.parameterize(stmt);
//执行了SQL 也就是说查询是没有批处理的
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
通过上文的代码可以发现:在使用批处理的时候有几点需要注意:
1.在批处理过程中不要有查询SQL,如果有需要使用不同的executor
2.在批量处理过程中,相同的SQL要一起处理,不要有不同SQL间隔添加到批处理中
3.在大量的执行过程中可以部分执行flush,最后执行事务提交
总结:
相关工具如下:
分析:
小结:
主要讲述了接下来Mybatis BatchExecutor源码分析详解,请大家指正~
参考资料和推荐阅读
1.链接: 参考资料.