问题:在Spring下怎么使用iBatis的批处理实现? 从4个层面分析这部分实现:
iBatis的基本实现
基于事务的iBatis的基本实现
基于事务的Spring+iBatis实现
基于回调方式的Spring+iBatis实现
startBatch() 开始批处理
executeBatch() 执行批处理
Java代码
public void create(List<Reply> replyList) {
try {
// 开始批处理
sqlMapClient.startBatch();
for (Reply reply: replyList) {
// 插入操作
sqlMapClient.insert("Reply.create", reply);
}
// 执行批处理
sqlMapClient.executeBatch();
} catch (Exception e) {
e.printStackTrace();
}
}
startTransaction() 开始事务
commitTransaction() 提交事务
endTransaction() 结束事务
Java代码
public void create(List<Reply> replyList) {
try {
// 开始事务
sqlMapClient.startTransaction();
// 开始批处理
sqlMapClient.startBatch();
for (Reply reply: replyList) {
// 插入操作
sqlMapClient.insert("Reply.create", reply);
}
// 执行批处理
sqlMapClient.executeBatch();
// 提交事务
sqlMapClient.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 结束事务
sqlMapClient.endTransaction();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
开始批处理 startBatch()
插入 insert()
执行批处理 executeBatch()
Java代码
public void create(List<Reply> replyList) {
if (!CollectionUtils.isEmpty(replyList)) {
// 注意使用同一个SqlMapClient会话
SqlMapClient sqlMapClient = sqlMapClientTemplate.getSqlMapClient();
try {
// 开始事务
sqlMapClient.startTransaction();
// 开始批处理
sqlMapClient.startBatch();
for (Reply reply : replyList) {
// 插入操作
sqlMapClient.insert("Reply.create", reply);
}
// 执行批处理
sqlMapClient.executeBatch();
// 提交事务 交给Spring统一控制
// sqlMapClient.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 结束事务
sqlMapClient.endTransaction();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Java代码
@SuppressWarnings("unchecked")
public void create(final List<Reply> replyList) {
// 执行回调
sqlMapClientTemplate.execute(new SqlMapClientCallback() {
// 实现回调接口
public Object doInSqlMapClient(SqlMapExecutor executor)
throws SQLException {
// 开始批处理
executor.startBatch();
for (Reply reply : replyList) {
// 插入操作
executor.insert("Reply.create", reply);
}
// 执行批处理
executor.executeBatch();
return null;
}
});
}
引用
public void create(final List<Reply> replyList)
这样做,就将事务处理的控制权完全交给了Spring! 简述:SqlMapClientCallback 回调接口
doInSqlMapClient(SqlMapExecutor executor) 回调实现方法
DataAccessException 最终可能抛出的异常
http://michael-softtech.iteye.com/blog/620433
最近遇到这样一个客户需求:需要向数据库里面一次插入几万条数据。系统的Persistence层用的是ibatis,
事务是通过spring管理。之前都是少量数据的操作,所以都是按照以下方式插入的:
Java代码
class Service extends SqlMapClientDaoSupport
{
public void insert(...)
{
getSqlMapClientTemplate().insert(..);
}
}
但是数据量大时,速度奇慢。于是找时间读了一下ibatis的源码,终于发现了问题所在,记录如下:
首先,过跟踪代码,发现sql的相关操作都有这样一个入口:
Java代码
public Object execute(SqlMapClientCallback action){ action.doInSqlMapClient(session);....}
上面的session是SqlMapSession的一个实例,而SqlMapSession继承了SqlMapExecutor接口,实际上以上的代码最终还是通过SqlMapExecutor的对应方法来实现(比如:session.insert(..)).
于是继续追踪SqlMapSession的实现类:SqlMapSessionImpl。发现这个类的所有JDBC操作都是通过代理类SqlMapExecutorDelegate来实现的(这个代理类比SqlExecutor多了事务管理的配置:有一个TransactionManager)。这个代理类在每个单独的操作时,都先有这样一条语句:
Java代码
trans = getTransaction(session);
autoStart = trans == null;
Java代码
trans = autoStartTransaction(session, autoStart, trans);
上述代码通过判断sutoStart来决定是不是开启一个事务。而autoStart是通过判断当前是不是已经有打开的事务
来赋值的。那么就可以理解了:如果当前操作没有在事务下面,那么自动开启(取出)一个事务;如果已经有了事务,那么 直接使用当前事务。如果要进行批量操作,那么就必须在调用之前开启一个事务。所以就简单了:
Java代码
public Object operate(final List<CardInfo> cardsToAdd, final List<AcctInfo> acctsToAdd, final List<AcctInfo> acctsToUpdate) throws DataAccessException{
Object obj=this.getSqlMapClientTemplate().execute(new SqlMapClientCallback(){
public Object doInSqlMapClient(SqlMapExecutor executor)
{
try{
getSqlMapClient().startTransaction();
executor.startBatch();...
后面的startBatch语句是通过使用jdbc的批处理来提高效率。这样就能顺利执行同一个事务下的批量操作了(注意:如果在批量startBatch之前没有开启事务,批处理是无效的)。