hibernate与jdbc事务共享的实现

背景:项目中基于JS脚本引擎,可以自行编写数据持久化脚本,为了验证脚本的正确性,从而进行Junit单元测试。JS脚本中所做的持久化工作由mybatis实现,为了方便准备测试数据及验证,使用hibernate做辅助测试工作。(所有工作全程由Spring托管)


目的:在基于Spring+Junit框架的同一个单元测试用例中,实现hibernate与jdbc的事务共享(一方持久化的内容,另一方可以查到验证并修改),测试完成后同时回滚不污染数据库。


遇到问题:虽然使用同一个数据源,但hibernate与mybatis或许使用的不是同一个连接,造成事务不能共享,在数据提交之前无法相互访问事务中未提交的数据。


最早的想法是,需要分别为hibernate与jdbc分别配置事务,同时交由Spring托管,但是在Junit中同时只能配置一种事务,事实证明,无论手动指定为哪一种,另一种持久化操作都会变得无法回滚(更不要提事务共享了),所以分别配置事务的方案搁浅,只能考虑使用同一个事务配置,就沿用了项目中针对事务的配置。基于同一个数据源的事务设为单例,完全托管于Spring,不回滚的问题解决。


但是出现了新的问题,就是之前提到的,事务无法共享。首选考虑能否把事务隔离的级别降低,但是不争气的报错:
org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: 仅 READ_COMMITTED 和 SERIALIZABLE 是有效的事务处理级

原来Oracle仅仅支持两种事务隔离级别,并不支持可能读取到脏数据的“READ_UNCOMMITTED”(其实要的就是所谓的脏数据(╯▽╰)),所以此方案搁浅。


最后想到了让hibernate进一步脱离Spring控制,手动从数据源中取Connection,然后传给SessionFactory创建Session。实现如下:

hibernate的配置中加入:

注:想要公用一个事务,必须使用同一个connection,想要使用同一个连接必须使用同一个dataSource对象,spring配置 useTransactionAwareDataSource 的目的就是为了保证connection 的唯一性


在测试框架中将dataSource和SessionFactory注入进来。

	@Resource
	DataSource dataSource;
	@Resource
	SessionFactory hib_sessionFactory;

在Before方法中使用dataSource创建连接和Session:

	conn = DataSourceUtils.getConnection(dataSource);
	this.session = hib_sessionFactory.openSession(conn);

在After方法中释放:

DataSourceUtils.releaseConnection(conn, dataSource);


配置完成之后,发现了一个奇怪现象:事务共享是单向的。具体现象为:通过JDBC持久化的内容,hibernate可以查到,但是反过来hibernate持久化之后,JDBC无法通过查询验证。纠结了半天后发现,在hibernate持久化之后,手动flush()一下,问题完美解决,可以实现事务内部共享,并且共同回滚。



参考资料:

hibernate事务与jdbc事务结合问题 http://wenku.baidu.com/view/d67df734b90d6c85ec3ac677.html

Spring事务的隔离级别http://blog.csdn.net/zhouwubin123/article/details/7199538

基于spring注解方式的事务管理配置http://blog.163.com/liyinhui20080527@126/blog/static/815232582012110113056123/

你可能感兴趣的:(Java)