【问题标题】:Spring tests inpact on service transactionsSpring 测试对服务事务的影响
【发布时间】:2011-11-01 01:12:16
【问题描述】:

我在我的 JUnit 测试中使用@Transactional(主要优点是在一个测试中回滚更改)但是我有一个小问题,这会影响我的服务事务。所以例如这是

我的服务:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class ServiceImpl

我的单元测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-context.xml" })
@Transactional
public class TestService 

@Test
public void testNumberTransaction() {
Entity a = new Entity();
Entity b = new Entity();
service.add(a);
service.add(b);
}

我很天真地期望 service.add() 有两个单独的事务,但除非我在测试方法上使用 @nontransactional 它在一个事务中运行(但它不会在测试后回滚)。

这是预期的吗?我可以通过一些配置来改变它吗?

谢谢

【问题讨论】:

    标签: spring testing transactions


    【解决方案1】:

    是的,这是意料之中的。 Propagation.REQUIRED(这是默认值)表示:如果存在,则在现有事务中执行。否则,创建一个事务并在方法结束时提交它。

    所以是的,如果整个测试方法是事务性的,那么两个服务调用都将在测试事务的上下文中执行。

    请注意,由于服务使用REQUIRED 进行注释,因此如果事务已经存在,它应该可以工作。这使得测试有效:它在现有事务的上下文中测试您的服务。如果您希望服务在其自己的专用事务中执行,则应使用REQUIRES_NEW 进行注释。但是当然,如​​果是这种情况,您将无法通过在事务中执行测试来回滚服务事务。

    【讨论】:

      【解决方案2】:

      默认情况下(REQUIRES 是默认传播行为)如果事务已经存在,您的服务方法将不会创建新的,而是加入已存在的。在您的情况下,它是由 Spring 测试框架创建的事务(将回滚的那个)。

      您可以通过将传播设置为REQUIRES_NEW 来更改此行为。但是,因为现在您的业务 add 方法在新事务中运行,一旦您离开此方法,事务将被提交而不是回滚。默认情况下,由于所有事务都加入到 JUnit 测试事务中,因此对数据库所做的所有更改都会回滚。

      【讨论】:

        【解决方案3】:

        @Transactional 定义了分界线。注释类等同于注释类的每个公共方法,分界线仍然是方法(在您的情况下,testNumberTransaction())。提交/回滚决定将在分界点进行,即从您的测试方法返回时。无论您指定REQUIRES 还是REQUIRES_NEW 传播,实际事务单元都是相同的,您的testNumberTransaction() 方法因此两个service.add(...) 调用将始终在同一个事务中执行。

        如果您的目标是始终在测试后回滚您的事务,那么只需删除 @Transactional 注释(或像您提到的那样放置 @nontransactional)就可以了。

        另一方面,如果您想为每个 service.add(...) 调用强制执行一个新事务,您可以为您的服务类创建一个包装器,其中您有一个带有 @Transactional(propagation) 注释的 add(...) 方法= Propagation.REQUIRES_NEW) 并从那里调用包装的 service 实例 add(...) 方法。或者,您可以在 Spring 测试上下文中添加一些声明性事务管理,为您的 service.add(...) 方法添加事务建议。阅读spring documentation 中的更多内容,了解如何使用<tx:XXX> 标签添加声明性事务支持。

        【讨论】:

        • 从测试方法中删除@Transactional 不会回滚事务:不会有任何用于测试的事务。事务将在服务方法被调用时启动,并在服务方法返回时提交。
        • 我的表述确实笨拙。删除@Transactional 注释将阻止事务管理器在java 层创建事务。但是,大多数数据库将始终为每个会话创建一个隐式事务,如果您将自动提交设置为关闭(默认情况下,IIRC),则没有事务的行为方式与总是回滚的事务相同。如果我错了,请纠正我。
        • 错误,没有。从 test 方法中删除 Transactional 将使测试在任何事务上下文之外运行,但 Spring 仍将为每个服务方法调用启动一个事务,因为 service 方法是用事务注释。因此,服务完成的实际事务工作(可能会更新某些数据库)将在事务中完成并提交,除非服务本身抛出运行时异常。
        • 假设服务方法是注解@Transactional,即。我的观点是,很多(如果不是大多数)数据库总是会为您执行的每个 SQL 命令创建一个事务,因此总会有一个事务上下文在起作用。您可以选择不让 Spring 获取该事务上下文(即,不注释 @Transactional),但它仍然存在。如果您将自动提交设置为“false”,它将永远不会提交并且您的更新将永远不会被反映。或者您可以将自动提交设置为“true”,在这种情况下,您的更新将始终被提交。
        • 服务用事务性注释的。它写在OP的问题中。
        猜你喜欢
        • 1970-01-01
        • 2013-10-04
        • 2010-12-15
        • 2021-10-15
        • 1970-01-01
        • 2021-03-15
        • 2017-12-04
        • 2019-04-22
        • 2010-11-01
        相关资源
        最近更新 更多