事务不适用于 Spring 3.1 – H2 – junit 4 – hibernate 3.2
Posted
技术标签:
【中文标题】事务不适用于 Spring 3.1 – H2 – junit 4 – hibernate 3.2【英文标题】:transactions not working with Spring 3.1 – H2 – junit 4– hibernate 3.2 【发布时间】:2013-01-29 03:05:16 【问题描述】:我有以下测试..
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/schedule-agents-config-context.xml")
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
public class H2TransactionNotWorkingTest extends SubmitAgentIntegratedTestBase
private static final int FIVE_SUBMISSIONS = 5;
@Autowired
private ApplicationSubmissionInfoDao submissionDao;
private FakeApplicationSubmissionInfoRepository fakeRepo;
@Before
public void setUp()
fakeRepo = fakeRepoThatNeverFails(submissionDao, null);
submitApplication(FIVE_SUBMISSIONS, fakeRepo);
@Test
@Rollback(true)
public void shouldSaveSubmissionInfoWhenFailureInDatabase()
assertThat(fakeRepo.retrieveAll(), hasSize(FIVE_SUBMISSIONS));
@Test
@Rollback(true)
public void shouldSaveSubmissionInfoWhenFailureInXmlService()
assertThat(fakeRepo.retrieveAll().size(), equalTo(FIVE_SUBMISSIONS));
...以及以下配置...
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:/db/h2-schema.sql" />
</jdbc:embedded-database>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionalSessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
</props>
</property>
<property name="namingStrategy">
<bean class="org.hibernate.cfg.ImprovedNamingStrategy"/>
</property>
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/>
<property name="packagesToScan" value="au.com.mycomp.life.snapp"/>
</bean>
<bean id="regionDependentProperties" class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="region-dependent-service-test.properties"/>
</bean
>
我还在 sql 脚本中将自动提交设置为 false
SET AUTOCOMMIT FALSE;
代码中没有 REQUIRES_NEW。 为什么回滚在测试中不起作用?
干杯 普拉宾
【问题讨论】:
【参考方案1】:我遇到了同样的问题,但我终于解决了,尽管我不使用 Hibernate(应该没关系)。
使其工作的关键是扩展正确的 Spring 单元测试类,即AbstractTransactionalJUnit4SpringContextTests
。注意类名中的“事务”。因此,一个工作事务单元测试类的框架如下所示:
@ContextConfiguration(locations = "classpath:/com/.../testContext.xml")
public class Test extends AbstractTransactionalJUnit4SpringContextTests
@Test
@Transactional
public void test()
关联的 XML 上下文文件包含以下项目:
<jdbc:embedded-database id="dataSource" type="H2" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
使用此设置可以正确回滚每种测试方法的修改。
问候,奥拉
【讨论】:
【参考方案2】:我遇到了类似的问题,我也在使用 TestNG + Spring 测试支持和 Hibernate。发生的情况是,Hibernate 在事务开始之前禁用连接上的自动提交,它会记住原始的自动提交设置:
org.hibernate.engine.transaction.internal.jdbc#JdbcTransaction:
@Override
protected void doBegin()
try
if ( managedConnection != null )
throw new TransactionException( "Already have an associated managed connection" );
managedConnection = transactionCoordinator().getJdbcCoordinator().getLogicalConnection().getConnection();
wasInitiallyAutoCommit = managedConnection.getAutoCommit();
LOG.debugv( "initial autocommit status: 0", wasInitiallyAutoCommit );
if ( wasInitiallyAutoCommit )
LOG.debug( "disabling autocommit" );
managedConnection.setAutoCommit( false );
catch( SQLException e )
throw new TransactionException( "JDBC begin transaction failed: ", e );
isDriver = transactionCoordinator().takeOwnership();
稍后,在回滚事务后,它将释放连接。这样做休眠也将恢复连接上的原始自动提交设置(以便可能分发相同连接的其他人从原始设置开始)。但是,在事务期间设置自动提交会触发显式提交,请参阅JavaDoc
在下面的代码中,您可以看到这种情况。发出回滚,最后在 releaseManagedConnection 中释放连接。这里将重新设置自动提交,从而触发提交:
org.hibernate.engine.transaction.internal.jdbc#JdbcTransaction:
@Override
protected void doRollback() throws TransactionException
try
managedConnection.rollback();
LOG.debug( "rolled JDBC Connection" );
catch( SQLException e )
throw new TransactionException( "unable to rollback against JDBC connection", e );
finally
releaseManagedConnection();
private void releaseManagedConnection()
try
if ( wasInitiallyAutoCommit )
LOG.debug( "re-enabling autocommit" );
managedConnection.setAutoCommit( true );
managedConnection = null;
catch ( Exception e )
LOG.debug( "Could not toggle autocommit", e );
这通常不应该是一个问题,因为 afaik 事务应该在回滚后结束。但更重要的是,如果我在回滚后发出提交,如果回滚和提交之间没有更改,则不应该提交任何更改,来自提交时的 javadoc:
使自上次提交/回滚以来所做的所有更改永久化 并释放此 Connection 当前持有的所有数据库锁 目的。仅当已启用自动提交模式时才应使用此方法 已禁用。
在这种情况下,回滚和提交之间没有变化,因为提交(通过重新设置自动提交间接触发)仅在几条语句之后发生。
解决方法似乎是禁用自动提交。这将避免恢复自动提交(因为它一开始没有启用),从而防止提交发生。您可以通过操作嵌入式数据源 bean 的 id 来做到这一点。 id不仅用于数据源的标识,还用于databasename:
<jdbc:embedded-database id="dataSource;AUTOCOMMIT=OFF" type="H2"/>
这将创建一个名为“dataSource”的数据库。额外的参数将由 H2 解释。 Spring 还将创建一个名为“dataSource;AUTOCOMMIT=OFF”的 bean。如果您依赖 bean 名称进行注入,您可以创建一个别名以使其更清晰:
<alias name="dataSource;AUTOCOMMIT=OFF" alias="dataSource"/>
(没有一种更简洁的方法来操作嵌入式数据库命名空间配置,我希望 Spring 团队可以更好地配置它)
注意:通过脚本 (
【讨论】:
【参考方案3】:试试这个:
移除 org.springframework.jdbc.datasource.DataSourceTransactionManager
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
并将其替换为 org.springframework.orm.jpa.JpaTransactionManager
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
或者你注入一个 EntityManagerFactory 代替...
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
然后你需要一个 EntityManagerFactory,如下所示
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
</bean>
【讨论】:
【参考方案4】:你还没有展示出拼图的所有部分。在这一点上,我的猜测是您的 ApplicationSubmissionInfoDao 是事务性的并且是自行提交的,尽管我认为如果一切配置正确,这将与测试事务冲突。要获得更多答案,请提出更完整的问题。最好的办法是发布SSCCE。
【讨论】:
【参考方案5】:谢谢瑞恩
测试代码是这样的。
@Test
@Rollback(true)
public void shouldHave5ApplicationSubmissionInfo()
for (int i = 0; i < 5; i++)
hibernateTemplate.saveOrUpdate(new ApplicationSubmissionInfoBuilder()
.with(NOT_PROCESSED)
.build());
assertThat(repo.retrieveAll(), hasSize(5));
@Test
@Rollback(true)
public void shouldHave5ApplicationSubmissionInfoAgainButHas10()
for (int i = 0; i < 5; i++)
hibernateTemplate.saveOrUpdate(new ApplicationSubmissionInfoBuilder()
.with(NOT_PROCESSED)
.build());
assertThat(repo.retrieveAll(), hasSize(5));
我发现使用 jdbc:embedded-database 定义的嵌入式数据库没有事务支持。当我使用 commons DBCP 定义数据源并将默认自动提交设置为 false 时,它起作用了。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" scope="singleton">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:~/snappDb"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
<property name="defaultAutoCommit" value="false" />
<property name="connectionInitSqls" value=""/>
</bean>
【讨论】:
【参考方案6】:以上都不适合我!
但是,我使用的堆栈是 [spring-test 4.2.6.RELEASE, spring-core 4.2.6.RELEASE, spring-data 1.10.1.发布]
问题是,使用带有 [SpringJUnit4ClassRunner.class] 注释的任何单元测试类将通过 spring 库设计产生自动回滚功能
检查***org.springframework.test.context.transaction.TransactionalTestExecutionListener*** >> ***isDefaultRollback***
要克服这种行为,只需在单元测试类中添加注释
@Rollback(value = false)
【讨论】:
以上是关于事务不适用于 Spring 3.1 – H2 – junit 4 – hibernate 3.2的主要内容,如果未能解决你的问题,请参考以下文章
带有忽略大小写的 JDBC URL 不适用于 H2 数据库连接
带有 Spring Security 核心和 CORS 插件的 grails REST API 不适用于 OPTIONS http 方法请求