【问题标题】:JPA: Nested transactional method is not rolled backJPA:嵌套事务方法不回滚
【发布时间】:2016-06-16 14:02:35
【问题描述】:

UPD 1:经过进一步研究,我认为以下信息可能有用:

  • 我通过 WildFly 9.0.2 上的 JNDI 查找获取数据源,然后将其“包装”到 HikariDataSource 的实例中(例如 return new HikariDataSource(jndiDSLookup(dsName)))。
  • 最终被使用的事务管理器是JTATransactionManager
  • 我没有以任何方式配置事务管理器。

原始问题:

我遇到了 JPA/Hibernate 和(可能)Spring-Boot 的问题,其中在从另一个类的事务方法调用的一个类的事务方法中引入的 DB 更改被提交,即使调用者方法中的更改被滚动返回(应该如此)。

这是我的交易服务

StuffService:

@Service
@Transactional(rollbackFor = IOException.class)
public class StuffService {

    @Inject private BarService barService;
    @Inject private StuffRepository stuffRepository;

    public Stuff updateStuff(Stuff stuff) {

        try {
            if (null != barService.doBar(stuff)) {
                stuff.setSomething(SOMETHING);
                stuff.setSomethingElse(SOMETHING_ELSE);
                return stuffRepository.save(stuff);
            }
        } catch (FirstCustomException e) {
            logger.error("Blah", e);
            throw new SecondCustomException(e.getMessage());
        }

        throw new SecondCustomException("Blah 2");
    }

    // other methods
}

BarService

@Service
@Transactional
public class BarService {

    @Inject private EntityARepository entityARepository;
    @Inject private EntityBRepository entityBRepository;

    /* 
     * updates existing entity A and persists new entity B. 
     */
    public EntityA doBar(Stuff stuff) throws FirstCustomException {

        EntityA a = entityARepository.findOne(/* some criteria */);
        a.setSomething(SOMETHING);

        EntityB b = new EntityB();
        b.setSomething(SOMETHING);
        b.setSomethingElse(SOMETHING_ELSE);

        entityBRepository.save(b);
        return entityARepository.save(a);
    }

// other methods
}

EntityARepositoryEntityBRepository 是非常相似的 Spring-Boot 存储库,定义如下:

public interface EntityARepository extends JpaRepository<EntityA, Long>{

    EntityA findOne(/* some criteria */);
}

FirstCustomException 扩展Throwable

SecondCustomException 扩展 RuntimeException

Stuff 实体是版本化的,并且每隔一段时间它就会由StuffService.updateStuff() 并发更新。在这种情况下,对stuff 实例之一的更改按预期回滚,但barService.doBar() 中发生的所有事情最终都会被提交。

这让我很困惑,因为这两种方法上的事务传播都应该是REQUIRED(默认方法)并且两种方法都属于不同的类,因此@Transactional 应该适用于两者。

我确实看到了Transaction is not completely rolled back after server throws OptimisticLockException1

但它并没有真正回答我的问题。

谁能告诉我发生了什么?

谢谢。

【问题讨论】:

  • 什么错误/异常导致其中一项服务回滚?
  • @Mubin: OptimisticLockException,这是由StaleObjectStateException引起的,完全可以预见。

标签: spring hibernate jpa jta


【解决方案1】:

这不是“嵌套”事务 - 这些服务在完全独立的事务中运行。如果您希望一个回滚影响另一个,您需要让它们参与同一个事务而不是启动它自己的事务。

或者,如果您的问题是传递给 doBar 方法的“stuff”版本存在问题并且您希望对其进行验证,那么您将需要对 stuff 实例执行一些会导致乐观锁检查的操作,并且因此,如果它是陈旧的,则会导致异常。见EntityManager.lock

【讨论】:

  • 嗨@Chris,感谢您的回答。我认为REQUIRED 传播级别barService.doBar() 将加入由Stuff Service.updateStuff() 启动的事务。您为什么认为它们与单独的交易相关联?
  • 是的, required 意味着它需要被包装在一个事务中。您的调用方法是控制该事务,还是非事务性的让容器为每个单独的调用启动/提交事务?
  • 这是一个事务方法调用另一个事务方法。我的理解是它们都应该回滚,因为被调用的方法应该加入由调用者方法启动的同一个事务。但现在不是这样,如果调用者方法有异常,只有那一个被回滚。
  • 如果配置正确,它们应该。您必须展示您的配置以确保您的所有资源都能够参与全局事务,并且所涉及的事务控制器能够将其标记为错误回滚。
  • 当然,除了带注释的服务类之外,事务配置还涉及什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-25
  • 2012-09-14
  • 2012-02-04
  • 2011-04-16
  • 1970-01-01
相关资源
最近更新 更多