2

I have a method with @Transaction calling a method with @Transaction( Propagation.REQUIRES_NEW) . Both the transactions are rolled back in case of exception in the parent method.

Parent Transaction:

@Transactional(propagation = Propagation.REQUIRED)
public void test() {
    SampleClassParent sampleClassParent = new SampleClassParent();
    sampleClassParent.setAddressId(2545L);
    sampleClassParent.setUserId(21660742L);
    getBaseDao().saveOrUpdate(sampleClassParent);
    newTransaction();

    // getting an exception purposefully
    User user = null;
    user.getId(); // Will throw a null pointer exception
}

Nested Transaction:

@Transactional(propagation = Propagation.REQUIRES_NEW)
private void newTransaction(){
    SampleClassNested sampleClassNested = new SampleClassNested();
    sampleClassNested.setCityId(15747L);
    sampleClassNested.setStoreId(5L);
    getBaseDao().saveOrUpdate(sampleClassNested);
}

SaveOrUpdate Method: (uses hibernate 3.3.2)

  // Wrapper around hibernate method
  public void saveOrUpdate(Object entity, boolean delayCommit) {
      getSessionFactory().getCurrentSession().saveOrUpdate(entity);
  }

All entries are created successfully when no exception is thrown. SampleClassParent and SampleClassNested are Hibernate Entity classes.

Here both the transactions are rolled back, but Ideally, Propagation.REQUIRES_NEW should suspend the existing transaction and create a new transaction. Why?

Abhinav
  • 31
  • 4
  • 3
    `@Transactional` doesn't work on a `private` method. See https://stackoverflow.com/questions/4396284/does-spring-transactional-attribute-work-on-a-private-method. Even for `public` methods, it only works when called from outside of your class. So your outside caller will have to make two calls. – David Lavender Jul 19 '17 at 08:09
  • The result is the same for both, public and private methods. The issue is that I have nested transactions. The outside method only calls the parent transaction. Also, If I call it using an outside method, will it not effectively mean the same thing (nested transactions)? – Abhinav Jul 19 '17 at 11:34

1 Answers1

0

From the provided code I would think it's because you call directly the method newTransaction(). For transaction management could be processed you have to give the container a chance to intercept the method call. That could be done by injection the bean and by calling the method from that injection point, kind of bean.newTransaction().

chalda
  • 702
  • 4
  • 18
  • I tried adding newTransaction() to a different service, and autowired the same. The behaviour is still the same. Didn't work. – Abhinav Jul 20 '17 at 12:57
  • ok, that was probably wrong hit. Another point that I think of is the usage of getSessionFactory().getCurrentSession(). I'm not sure how it's handled but if there is used the same session in both methods the db connection could be the same - meaning bound to the original global transaction. Even the new global transaction is really started, the db update is done with connection of the old one. What is implementation of the getSessionFactory().getCurrentSession() or how about autowiring it? – chalda Jul 24 '17 at 13:58
  • I have not modified the implementation of the hibernate session classes, I have simply used getCurrSessionFromFactory().saveOrUpdate(entity) of the base hibernate class. – Abhinav Aug 03 '17 at 05:19
  • I'm sorry just I don't understand how the method `getSessionFactory()` looks. Nevertheless my main point is - if the getBaseDao()#getSessionFactory().getCurrentSession() returns the same session then you are using the same connection with already opened transaction and even the new global transaction is started the transaction on connection is committed as one unit of work. It would be good to check if it's so or not. Maybe there is some misconfiguration of hibernate (e.g.hibernate.transaction.manager_lookup_class or hibernate.transaction.factory_class is not set for JTA?) – chalda Aug 03 '17 at 07:36