如何防止JPA回滚交易?

时间:2009-11-09 15:28:35

标签: java hibernate spring jpa struts

调用的方法:
1. Struts行动
2.服务类方法(由@Transactional注释)
3. Xfire webservice调用

使用Spring配置包括struts(DelegatingActionProxy)和事务在内的所有内容。

使用JPA / Hibernate完成持久性。

有时web服务会抛出未经检查的异常。我捕获此异常并抛出一个已检查的异常。我不希望事务回滚,因为Web服务异常会更改当前状态。我已经注释了这样的方法:

@Transactional(noRollbackFor={XFireRuntimeException.class, Exception.class})
public ActionForward callWS(Order order, ....) throws Exception
  (...)
  OrderResult orderResult = null;

  try {
    orderResult = webService.order(product, user)
  } catch (XFireRuntimeException xfireRuntimeException) {
    order.setFailed(true);
    throw new WebServiceOrderFailed(order);
  } finally {
    persist(order);
  }
}

我仍然遇到这个例外:

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly

当我尝试使用junit重现此事务时,事务未标记为回滚,并且仍可以提交事务。

如何让Spring不回滚事务?

2 个答案:

答案 0 :(得分:9)

管理为此问题创建测试用例:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:web/WEB-INF/spring/applicationContext.xml",
        "file:web/WEB-INF/spring/services.xml"})
@Transactional
public class DoNotRollBackTest {
    @Autowired FakeService fakeService;

    @Test
    @Rollback(false)
    public void testRunXFireException() {
        fakeService.doSomeTransactionalStuff();
    }
}

FakeService:

@Service
public class FakeService {
    @Autowired private EcomService ecomService;
    @Autowired private WebService webService;

    @Transactional(noRollbackFor={XFireRuntimeException.class})
    public void doSomeTransactionalStuff() {
        Order order = ecomService.findOrderById(459);

        try {
            webService.letsThrowAnException();
        } catch (XFireRuntimeException e) {
            System.err.println("Caugh XFireRuntimeException:" + e.getMessage());
        }

        order.setBookingType(BookingType.CAR_BOOKING);
        ecomService.persist(order);
    }
}

WebService的:

@Transactional(readOnly = true)
public class WebService {
    public void letsThrowAnException() {
        throw new XFireRuntimeException("test!");
    }
}

这将重新创建回滚异常。

然后我意识到事务可能在WebService.letsThrowAnException中被标记为rollbackOnly,因为WebService也是事务性的。我转到注释:

@Transactional(noRollbackFor={XFireRuntimeException.class})
    public void letsThrowAnException() {

现在事务没有被回滚,我可以将更改提交给Order。

答案 1 :(得分:3)

你不能在Spring可以看到它的情况下抛出异常。在这种情况下,您不能抛出WebServiceOrderFailed()。解决方案是将代码拆分为两种方法。第一种方法执行错误处理并返回异常,外部方法创建事务。

[编辑]关于noRollbackFor:尝试将Exception.class替换为WebServiceOrderFailed.class