7

I have an EntityManager object maintained by the Spring framework and I inject it in whatever DAO class I want using the @PersistenceContext annotation like this..

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

I use those DAO classes to save in the database something like this..

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
         }
     }
}

Now, both the daos here use the same EntityManager injected through @PersistenceContext(unitName="entityManager"). Now, if an exception occurs after setXYZ(), then I would like to rollback even the saved XYZ entity. But, how do I get the EntityManager from that?

If all the daos hold the same object, then can I just call the getTransaction().rollback() method of the EntityManager class? Does the getTransaction() return a new transaction or any transaction that is currently associated with EntityManager?

iCantSeeSharp
  • 3,880
  • 4
  • 42
  • 65
JavaTechnical
  • 8,846
  • 8
  • 61
  • 97

4 Answers4

11
  1. If you used Spring AOP to manage transaction, and the configuration and annotation is used right, the default effect is the transaction would be rolled back when the runtime exception occurs.

  2. If you managed transaction manually, you can roll back transaction like this:

    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();
    

See more at: 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

iCantSeeSharp
  • 3,880
  • 4
  • 42
  • 65
huangyp
  • 119
  • 6
8

It will be rollback once you throw any RuntimeException from a method marked as @Transactional like below:

By default, all RuntimeExceptions rollback transaction where as checked exceptions don't:

@Transactional(rollbackFor={MyRuntimeException.class, AnotherRuntimeException.class})
public SomeVal someTransactionalMethod(){
   ...
}
PKumar
  • 516
  • 9
  • 20
1

Just don't catch the exception. Let it bubble. Spring will automatically rollback the transaction if a runtime exception is thrown from a transactional method call. And the caller will at least know that something bad happen, instead of thinking everything went fine.

Anyway, your catch block will probably not catch anything, because most of the exceptions happen at flush time, and flush mainly happens just before commit, in the Spring transaction interceptor. Remember than persisting an entity doesn't execute an insert query immediately. It just tells Hibernate that, before the end of the transaction, an insert will have to be executed.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • But in the above example, the XYZ entity is saved, and if an exception occurs after it has saved i.e. while saving the MyEntity then, does XYZ entity save gets rollback? Which is exactly what I want. – JavaTechnical Oct 05 '14 at 07:22
  • 1
    Of course. That's the definition of a transaction. Either it commits, and all the inserts, updates and deletes executed inside the transaction succeed, or it rollbacks, and all the inserts, updates and deletes executed inside the transaction are canceled. – JB Nizet Oct 05 '14 at 07:25
  • Now, in my case I am actually using the same `unitName` for EntityManager, can I say that both XYZ and MyEntity gets saved in the same transaction? – JavaTechnical Oct 05 '14 at 07:26
  • 1
    No. The transaction has nothing to do with JPA unit names. You could use two unit names in a single transaction, and you can of course have many transactions targetting the same JPA unit name. In Spring, transactions are typically demarcated by annotating bean methods with `@Transactional`, and are handled by a transaction manager (typically, with JPA, a JpaTransactionManager). Spring has a whole chapter explaining transactions: http://docs.spring.io/spring-framework/docs/4.1.x/spring-framework-reference/html/transaction.html. – JB Nizet Oct 05 '14 at 07:33
  • But for me actually, rolling back didn't happen when an exception occurred at MyEntity after saving the XYZ, MyEntity is just not saved, however XYZ remained in the db – JavaTechnical Oct 05 '14 at 07:40
  • But you didn't tell us anything about the way transactions are handled in your app. When does the transaction start? When does it stop? How do you handle/configure transactions? – JB Nizet Oct 05 '14 at 07:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/62479/discussion-between-javatechnical-and-jb-nizet). – JavaTechnical Oct 05 '14 at 07:41
  • What if I want to throw a runtime exception at the end of the method? Will that also rollback the transaction; even if there was no error during saving an entity. – Akki Sep 28 '20 at 17:35
1

To rollback a transaction you can use @Transaction annotation. You can either implement it on method level or class level.

Method level example:

@Transactional(rollbackFor = {YourDesiredException.class, SomeOtherException.class})
void yourMethod(datatype param1,...){
     //your transaction that may throw exception
}

Class level example:

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

void method1(){
    //transaction 1
}

void method2(){
   //transaction 2
}

}

Class level @Transactional(rollbackFor = Exception.class) will rollback all the transaction happened at the class level where as method level will only rollback transactions happened in that method.

PS: Do not use try-catch block (ie, do not catch the exception) and let the exception propagate.

mohammed saquib
  • 140
  • 1
  • 6