Question

I am trying to understand Spring Transaction concept. As shown below, i've to insert data into two different databases (iSeries and DB2) but our iSeries version doesn't support Two-Phase commit. The requirement is, Transaction should only be committed if both the Inserts are successful else it should rollback.

If i use the Propagation as REQUIRED or REQUIRES_NEW, i get the error "An illegal attempt to commit a one phase capable resource with existing two phase capable resources has occurred".

But if i use NOT_SUPPORTED or SUPPORTS, it works fine (i.e. Transaction rolls back if one of the Insert fails, else it commits if both the Inserts are successful).

My understanding is, if Propagation = SUPPORTS / NOT_SUPPORTED, then Transaction won't even be started in the below scenario. Hence, both the inserts might happen independently in the two different databases and the whole Transaction shouldn't roll back if one of them fails.

But Propagation = SUPPORTS / NOT_SUPPORTED works as per my requirement. Can someone please explain this? Thanks in advance.

@Resource
private SessionFactory db2SessionFactory = null;

@Resource
private SessionFactory iSeriesSessionFactory = null;

@Transactional(propagation = Propagation.REQUIRED)
public void insert()
{
   insertDB1();
   insertDB2();
}

insertDB1()
{
   db2SessionFactory .getCurrentSession().saveOrUpdate(obj1);
}

insertDB2()
{
   iSeriesSessionFactory.getCurrentSession().saveOrUpdate(obj2);
}
Was it helpful?

Solution

Ok, I'll take a crack at explaining what I think is happening. I stress, this is supposition on my part. It may be that I'm entirely wrong.

Firstly, I've never had to specify a Propagation for the @Transactional annotation. Required is a good standard. If you are getting exceptions, its usually because you are not manipulating your database correctly. However this may not be the case here, because I've never had to work with more than one session factory before.

What I think is happening when you specify SUPPORTS or NOT_SUPPORTED is that the saves are occurring non-transactionally Just because the saves are occurring non-transactionally, it doesn't mean that if a certain type of error occurs, the whole session won't roll back, because that's obviously what's happening.

When you use REQUIRED or REQUIRES_NEW you are creating a new transaction with each with its own JDBC connection (obviously) but using the same transaction session. That's why you are getting your illegal attempt exception. The transactional session has been created for a two-phase resource but you are trying to use the same session to save to the one-phase database.

Propagation really only comes into play if you have 2 methods annotated @Transactional with one calling the other like this:

@Transactional
public void method1(){
    method2();
}

@Transactional
public void method2(){
    //do some transactional stuff here
}

Your propagation level will determine if a new session for method1 is created, if the existing session is used for method2, if a new session is created for method2, or if the entire thing is executed non-transactionally.

OTHER TIPS

Only JTA support two-phase commit transaction. Check if you use JtaTransactionManager.

It works fine when you use NOT_SUPPORTED or SUPPORTS because it works non-transactionally. See org.springframework.transaction.annotation.Propagation javadoc.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top