【问题标题】:@Transactional(propagation=Propagation.REQUIRES_NEW) opens two transactions?@Transactional(propagation=Propagation.REQUIRES_NEW) 打开两个事务?
【发布时间】:2017-11-18 22:58:29
【问题描述】:

我们发生了一个生产事件,导致一堆线程死锁,服务器停止工作。为了尝试调查,我用不同的 spring 事务传播测试了一些东西,如果我没记错的话,如果根本没有现有事务,REQUIRES_NEW 传播将启动两个连接。它是否正确??我尝试了谷歌搜索,但没有找到有关此的信息。

我做了一个测试。这是一个示例类:

package test;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class TheService {

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void doSomething() {
        System.out.println("Here I am doing something.");
    }
}

这是我做的单元测试:

package test;

import javax.annotation.Resource;

import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;

@ContextConfiguration(locations = {"classpath:test.xml"})
public class TheServiceTest extends AbstractTransactionalJUnit4SpringContextTests {

    @Resource
    private TheService theService;

    @Test
    public void test() {
        theService.doSomething();
    }

}

最后但同样重要的是,这是我的测试 xml:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"
    default-autowire="byName">

    <context:component-scan base-package="test" />

    <tx:annotation-driven transaction-manager="transactionManager" />

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="operator.entityManagerFactory" />
    </bean>

    <bean id="operator.entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="operatorPersistenceUnit" />
        <property name="dataSource" ref="operator.dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false" />
                <property name="generateDdl" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
            </bean>
        </property>
    </bean>

    <bean id="operator.dataSource" class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close">
        <property name="driverClassName" value="org.h2.Driver" />
        <property name="url" value="jdbc:h2:mem:operator" />
        <property name="username" value="sa" />
        <property name="password" value="" />
        <property name="maxActive" value="1" /> <!-- NOTE -->
    </bean>
</beans>

我想要一个方法的 REQUIRES_NEW 的原因是因为它不能从它获得任何脏读是至关重要的,并且它可以在另一个事务内部和外部执行。

如果我将 maxActive 属性保持为 1,则此测试将死锁并且永远不会打印任何内容。但是,如果我将其更改为 2,则测试将通过。

这是一个令人担忧的原因是,即使我将 maxActive 设置为更高的值,如果有足够多的线程等待执行此方法,它们最终都会各自占用一个连接并等待第二个连接。

我做错了吗?我有什么误解吗?

感谢您的帮助!谢谢!

【问题讨论】:

  • 不,它不会打开 2 个连接。它在您的测试中确实如此,因为 1 由您的测试打开(它是事务性的)并且在您的服务中打开了一个新的,因为 REQUIRES_NEW..
  • 为什么我的测试打开了一个?有没有办法避免这种情况?此外,有时要查看某个方法是否在事务中,我会添加 new Exception().printStackTrace() 并查找 TransactionInterceptor。这个,在测试方法中添加的时候看不到。
  • 你认为AbstractTransactionalJUnit4SpringContextTests 做了什么......类名的Transactional 部分是有原因的。还有更多处理事务的方法。对于由手动启动/停止事务的TransactionalTestExecutionListener 完成的测试。如果您不希望您的测试是事务性的,请不要扩展 AbstractTransactionalJUnit4SpringContextTests

标签: java spring multithreading transactions


【解决方案1】:

它与propagation=REQUIRES_NEW 无关,默认情况下不会打开 2 个连接。问题是您正在扩展AbstractTransactionalJUnit4SpringContextTests

随着您的测试用例扩展 AbstractTransactionalJUnit4SpringContextTests,如您所见,它是 @Transactional。此事务用于测试,由TransactionalTestExecutionListener 管理。

因此,当您开始测试时,会发生什么情况,在测试方法执行之前,测试框架会启动一个事务。接下来,您调用您的服务,该服务由于使用@Transactional(propagation=REQUIRES_NEW) 注释而启动另一个事务。

修复很简单,不要扩展 AbstractTransactionalJUnit4SpringContextTests,只需使用 @RunWith(SpringRunner.class) 注释您的类。

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:test.xml"})
public class TheServiceTest { ... }

【讨论】:

  • 你完全正确。我的错。感谢您的帮助,好先生!
猜你喜欢
  • 2013-03-23
  • 2011-10-05
  • 1970-01-01
  • 2020-06-23
  • 2020-12-14
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
  • 2021-10-14
相关资源
最近更新 更多