事务不适用于 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*** &gt;&gt; ***isDefaultRollback***

要克服这种行为,只需在单元测试类中添加注释 @Rollback(value = false)

【讨论】:

以上是关于事务不适用于 Spring 3.1 – H2 – junit 4 – hibernate 3.2的主要内容,如果未能解决你的问题,请参考以下文章

事务管理似乎不适用于 Spring 测试

带有忽略大小写的 JDBC URL 不适用于 H2 数据库连接

CSS 过渡不适用于 FF 中的 top 属性

带有 Spring Security 核心和 CORS 插件的 grails REST API 不适用于 OPTIONS http 方法请求

java - 如何在java桌面应用程序中使用spring(事务)和hibernate创建嵌入式H2 DB?

点燃不会从 spring-boot 2.0.5 开始 - h2 属性 NESTED_JOINS 不存在