ibatis 批处理
2016-07-06 16:51
190 查看
Ibatis中进行批量操作
iBatis整理——iBatis批处理实现(Spring环境)
http://www.cnblogs.com/sunwei2012/archive/2010/11/26/1888497.html最近做一个小项目,用到Spring+iBatis。突然遇到一个很久远,却很实在的问题:在Spring下怎么使用iBatis的批处理实现?
大概是太久没有写Dao了,这部分真的忘得太干净了。
从4个层面分析这部分实现:
iBatis的基本实现
基于事务的iBatis的基本实现
基于事务的Spring+iBatis实现
基于回调方式的Spring+iBatis实现
1.iBatis的基本实现
iBatis通过SqlMapClient提供了一组方法用于批处理实现:
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();
}
}
这是基于iBatis的最基本实现,如果你一步一步debug,你会发现:其实,数据库已经执行了插入操作!
因此,除了这两个核心方法外,你还需要开启事务支持。否则,上述代码只不过是个空架子!
2.基于事务的iBatis的基本实现
事务处理:
startTransaction() 开始事务
commitTransaction() 提交事务
endTransaction() 结束事务
我们以insert操作为例,把它们结合到一起:
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();
}
}
}
replyList是一个List,要把这个List插入到数据库,就需要经过这三个步骤:
开始批处理 startBatch()
插入 insert()
执行批处理 executeBatch()
如果要在Spring+iBatis中进行批处理实现,需要注意使用同一个sqlMapClient!同时,将提交事务的工作交给Spring统一处理!
3.基于事务的Spring+iBatis实现
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();
}
}
}
}
注意使用同一个sqlMapClient:
SqlMapClient sqlMapClient = sqlMapClientTemplate.getSqlMapClient();
如果直接sqlMapClientTemplate执行insert()方法,将会造成异常!
想想,还有什么问题?其实问题很明显,虽然解决了批处理实现的问题,却造成了事务代码入侵的新问题。
这么做,有点恶心!
除此之外,异常的处理也很恶心,不能够简单的包装为 DataAccessException 就无法被Spring当作统一的数据库操作异常做处理。
4.基于回调方式的Spring+iBatis实现
如果观察过Spring的源代码,你一定知道,Spring为了保持事务统一控制,在实现ORM框架时通常都采用了回调模式,从而避免了事务代码入侵的可能!
修改后的代码如下:
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;
}
});
}
注意,待遍历的参数replyList需要加入final标识!即,待遍历对象不能修改!
引用
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之前没有开启事务,批处理是无效的)。
相关文章推荐
- 循环神经网络(RNN, Recurrent Neural Networks)介绍
- U-Boot 的移植入门(2)——nand flash识别与操作
- C++虚函数和纯虚函数
- Linux保存修改vi指令
- iOS app性能优化的那些事(一)
- JMeter基础之-使用技巧
- jmeter 脚本增强(参数化)
- JAVA的节点流和处理流
- Linux lsof命令使用小结
- 带 IK 分词器的 Luke 和 搜索应用服务器solr
- 实现jqgrid 行编辑,级联查询,并点击按钮保存
- 将自己写的Python代码打包放到PyPI上
- 112. Path Sum
- webuploader文件接收服务端
- solution Of 1018. Public Bike Management (30)
- [书目20160706]成功销售实用经典10步骤(美国培训与发展协会实用经典10步系列)
- python vs ruby
- android_结构
- 阿里面经之解答 by cmershen(4)——线程池
- nginx如何防止ddos***