EJB Hibernate嵌套事务未回滚求助:已标注@ApplicationException(rollback=true)
问题:嵌套事务异常后未按预期回滚
编写测试代码验证嵌套事务更新两个实体的逻辑,启动嵌套事务尝试更新两个实体,但transaction3()方法抛出异常后,相关实体的更新未按预期回滚,相关配置与代码如下:
persistence.xml
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="sac"> <jta-data-source>java:jboss/datasources/sacDS</jta-data-source> <class>org.hibernate.envers.DefaultRevisionEntity</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/> <property name="hibernate.show_sql" value="false"/> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.hbm2ddl.auto" value="validate"/> </properties> </persistence-unit> </persistence>
EnvioException
@javax.ejb.ApplicationException(rollback=true) public class EnvioException extends javax.ws.rs.WebApplicationException { private static final long serialVersionUID = -990622645054458959L; }
REST API
@Path("/transactionService") public class TransactionService { @EJB TestTransaction testTransaction; @Path("/transaction") @GET @SemAutenticacao public void transaction(@Context HttpHeaders headers) throws IOException, SQLException { testTransaction.transaction1(); } }
核心业务类 TestTransaction
@Stateless public class TestTransaction { @Inject protected EntityManager em; public void transaction1() throws IOException, SQLException { transaction2(); } @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void transaction2() throws IOException, SQLException { TransactionEntity transactionEntity = em.find(TransactionEntity.class, 1L); transactionEntity.setNome("Joe"); try { transaction3(); } catch(EnvioException e) { System.out.println("Exception Handled here!"); } } @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void transaction3() throws EnvioException { Transaction2Entity transaction2Entity = em.find(Transaction2Entity.class, 2L); transaction2Entity.setNome("Joe"); throw new EnvioException(); } }
解决方案
问题根源
- 同一Bean内方法调用不触发事务代理:无状态EJB内部的方法调用不会经过容器的事务拦截器,因此
transaction3()上的@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)完全不生效,它会和transaction2()共用同一个事务。 - 异常捕获导致事务未回滚:
EnvioException被标记为@ApplicationException(rollback=true),但你在transaction2()中捕获了该异常,容器不会自动触发当前事务的回滚,导致所有实体更新都被提交。
修复步骤
1. 拆分EJB,确保事务属性生效
将transaction3()方法移到独立的无状态EJB中,这样跨Bean调用时会经过容器代理,REQUIRES_NEW才会创建新事务:
@Stateless public class Transaction3Service { @Inject protected EntityManager em; @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void transaction3() throws EnvioException { Transaction2Entity transaction2Entity = em.find(Transaction2Entity.class, 2L); transaction2Entity.setNome("Joe"); throw new EnvioException(); } }
在原TestTransaction中注入这个新EJB并调用:
@Stateless public class TestTransaction { @Inject protected EntityManager em; @EJB private Transaction3Service transaction3Service; public void transaction1() throws IOException, SQLException { transaction2(); } @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void transaction2() throws IOException, SQLException { TransactionEntity transactionEntity = em.find(TransactionEntity.class, 1L); transactionEntity.setNome("Joe"); try { transaction3Service.transaction3(); } catch(EnvioException e) { System.out.println("Exception Handled here!"); // 若需要回滚transaction2的事务,手动标记 TransactionSynchronizationRegistry registry = InitialContext.doLookup("java:comp/TransactionSynchronizationRegistry"); registry.setRollbackOnly(); } } }
2. 手动控制事务回滚
transaction3()所在的新事务会因异常自动回滚,transaction2Entity的更新会被撤销。- 如果需要
transaction2()的事务也回滚,在捕获异常后通过TransactionSynchronizationRegistry手动标记事务为仅回滚状态,容器会在方法结束时回滚该事务。
3. 验证事务边界
确保所有需要独立事务的方法都通过跨Bean调用触发,避免同一Bean内的直接调用,这是EJB事务属性生效的前提。
内容的提问来源于stack exchange,提问作者Renato
相关产品推荐
相关产品推荐

