在JPA中如何回滚事务?

7

我有一个由Spring框架维护的EntityManager对象,我可以使用@PersistenceContext注解将其注入到任何DAO类中,例如:

@PersistenceContext(unitName="entityManager")
private EntityManager em;

我使用这些DAO类将以下内容保存在数据库中...
class MyClass
{
    @Resource(name="myDao")
    private MyDao dao;

    @Resource(name="myAnotherDao")
    private MyAnotherDao anotherDao;

    public void save(String s1,String s2)
    {
        try
        {
             MyEntity m=new MyEntity();
             m.setName(s1);
             // .. and so on ..

             XYZ x=new XYZ();
             x.setDEF(s2);

             anotherDao.save(x);

             m.setXYZ(x);
             // .. some other stuff .. //
             dao.saveEntity(m);
         }
         catch(Exception e)
         {
             // I would like to rollback the transaction
         }
     }
}

现在,这两个dao都使用通过@PersistenceContext(unitName="entityManager")注入的相同的EntityManager。 现在,如果在setXYZ()之后发生异常,那么我想回滚保存的XYZ实体。 但是,我该如何从中获取EntityManager呢?
如果所有的dao都持有相同的对象,那么我可以调用EntityManager类的getTransaction().rollback()方法吗?getTransaction()是否返回新事务或当前与EntityManager关联的任何事务?
4个回答

11
  1. 如果你使用Spring AOP来管理事务,并且配置和注解使用正确,那么默认效果是当运行时异常发生时,事务将会被回滚。

  2. 如果你手动管理事务,可以像这样回滚事务:

  3. EntityManager em = createEntityManager();
    
    try {
    
        em.getTransaction().begin();
        // Do something with the EntityManager such as persist(), merge() or remove()
        em.getTransaction().commit();
    } catch(Exception e) {
    
        em.getTransaction().rollback();
    }
    
    em.close();
    

更多信息请参见: http://en.wikibooks.org/wiki/Java_Persistence/Transactions http://www.developerscrappad.com/547/java/java-ee/ejb3-x-jpa-when-to-use-rollback-and-setrollbackonly/#sthash.jx3XlK5m.dpuf


8

如果您从标记为@Transactional的方法中抛出任何RuntimeException,它将回滚:

默认情况下,所有RuntimeException都会回滚事务,而受检异常不会:

@Transactional(rollbackFor={MyRuntimeException.class, AnotherRuntimeException.class})
public SomeVal someTransactionalMethod(){
   ...
}

1

不要捕获异常,让它冒泡。如果从事务方法调用中抛出运行时异常,Spring将自动回滚事务。调用者至少会知道发生了什么糟糕的事情,而不是认为一切都很好。

无论如何,您的catch块可能不会捕获任何异常,因为大多数异常发生在flush时间,在Spring事务拦截器中,flush主要发生在提交之前。请记住,持久化实体并不会立即执行插入查询。它只是告诉Hibernate,在事务结束之前,必须执行插入操作。


1
当然。这就是事务的定义。它要么提交,所有在事务内执行的插入、更新和删除操作都成功,要么回滚,所有在事务内执行的插入、更新和删除操作都被取消。 - JB Nizet
1
不,事务与JPA单元名称无关。您可以在单个事务中使用两个单元名称,并且当然可以有许多事务针对同一JPA单元名称。在Spring中,通常通过在bean方法上注释@Transactional来标记事务,并由事务管理器(通常使用JPA,即JpaTransactionManager)处理。 Spring有一个完整的章节解释事务:http://docs.spring.io/spring-framework/docs/4.1.x/spring-framework-reference/html/transaction.html。 - JB Nizet
但对我来说,实际上,在保存XYZ后,如果在MyEntity发生异常,回滚并没有发生,只是MyEntity没有被保存,而XYZ仍然存在于数据库中。 - JavaTechnical
但是您没有告诉我们有关应用程序中事务处理方式的任何信息。交易何时开始?何时停止?您如何处理/配置交易? - JB Nizet
让我们在聊天中继续这个讨论 - JavaTechnical
显示剩余3条评论

1
要回滚事务,您可以使用@Transaction注释。您可以在方法级别或类级别上实现它。
方法级别示例:
@Transactional(rollbackFor = {YourDesiredException.class, SomeOtherException.class})
void yourMethod(datatype param1,...){
     //your transaction that may throw exception
}

类级别示例:

@Transactional(rollbackFor = {YourDesiredException.class, SomeOtherException.class})
public class SomeClass throws YourDesiredException{

void method1(){
    //transaction 1
}

void method2(){
   //transaction 2
}

}

类级别的 @Transactional(rollbackFor = Exception.class) 将会回滚在该类中发生的所有事务,而方法级别仅会回滚在该方法中发生的事务。

PS:不要使用 try-catch 块(即,不要捕获异常)并让异常传播。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接