在介绍Mybatis批处理之前,首先回顾一下PreparedStatement的批处理:
//加载驱动
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();
上文代码中可以看到可以有效减少网络交互次数,如果插入100条记录,就可以减少99次网络交互;这样效果就比较明显了。下面看一下Mybatis中的批处理是如何处理的。
@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);
}
}
本文来自网易实践者社区,经作者张伟授权发布。