背景:项目中基于JS脚本引擎,可以自行编写数据持久化脚本,为了验证脚本的正确性,从而进行Junit单元测试。JS脚本中所做的持久化工作由mybatis实现,为了方便准备测试数据及验证,使用hibernate做辅助测试工作。(所有工作全程由Spring托管)
目的:在基于Spring+Junit框架的同一个单元测试用例中,实现hibernate与jdbc的事务共享(一方持久化的内容,另一方可以查到验证并修改),测试完成后同时回滚不污染数据库。
遇到问题:虽然使用同一个数据源,但hibernate与mybatis或许使用的不是同一个连接,造成事务不能共享,在数据提交之前无法相互访问事务中未提交的数据。
最早的想法是,需要分别为hibernate与jdbc分别配置事务,同时交由Spring托管,但是在Junit中同时只能配置一种事务,事实证明,无论手动指定为哪一种,另一种持久化操作都会变得无法回滚(更不要提事务共享了),所以分别配置事务的方案搁浅,只能考虑使用同一个事务配置,就沿用了项目中针对事务的配置。基于同一个数据源的事务设为单例,完全托管于Spring,不回滚的问题解决。
原来Oracle仅仅支持两种事务隔离级别,并不支持可能读取到脏数据的“READ_UNCOMMITTED”(其实要的就是所谓的脏数据(╯▽╰)),所以此方案搁浅。
最后想到了让hibernate进一步脱离Spring控制,手动从数据源中取Connection,然后传给SessionFactory创建Session。实现如下:
hibernate的配置中加入:
注:想要公用一个事务,必须使用同一个connection,想要使用同一个连接必须使用同一个dataSource对象,spring配置 useTransactionAwareDataSource 的目的就是为了保证connection 的唯一性
在测试框架中将dataSource和SessionFactory注入进来。
@Resource
DataSource dataSource;
@Resource
SessionFactory hib_sessionFactory;
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/