iBatis整理——Spring环境下批处理实现

最近做一个小项目,用到Spring+iBatis。突然遇到一个很久远,却很实在的问题:在Spring下怎么使用iBatis的批处理实现?

大概是太久没有写Dao了,这部分真的忘得太干净了。

从4个层面分析这部分实现:
  1. iBatis的基本实现
  2. 基于事务的iBatis的基本实现
  3. 基于事务的Spring+iBatis实现
  4. 基于回调方式的Spring+iBatis实现


1.iBatis的基本实现
iBatis通过SqlMapClient提供了一组方法用于批处理实现:
  1. startBatch() 开始批处理
  2. executeBatch() 执行批处理

代码如下:
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的基本实现
事务处理:
  1. startTransaction() 开始事务
  2. commitTransaction() 提交事务
  3. endTransaction() 结束事务


我们以insert操作为例,把它们结合到一起:
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插入到数据库,就需要经过这三个步骤:
  1. 开始批处理 startBatch()
  2. 插入      insert()
  3. 执行批处理 executeBatch()

如果要在Spring+iBatis中进行批处理实现,需要注意使用同一个sqlMapClient!同时,将提交事务的工作交给Spring统一处理!

3.基于事务的Spring+iBatis实现
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框架时通常都采用了回调模式,从而避免了事务代码入侵的可能!
修改后的代码如下:
@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!
简述:
  1. SqlMapClientCallback 回调接口
  2. doInSqlMapClient(SqlMapExecutor executor) 回调实现方法
  3. DataAccessException 最终可能抛出的异常

通过上述修改,最终能够解决第三种实现方式中的种种不足!

Spring对iBatis提供的支持还是不够完善,即便是现在最新的Spring3.0.4。最开始,本打算用Spring3.0+iBatis3.0,结果Spring报错,说找不到“com.ibatis.xxxxx”完全是iBatis2.x的包路径!汗颜~ 还是Hibernate比较得宠!

做个小记录,呵呵!

你可能感兴趣的:(spring,ibatis,orm)