PDA

View Full Version : Can Spring control transactions at all when using Hibernate?


kidahl2
Apr 1st, 2005, 07:21 AM
When looking at previous threads, it seems to me that PROPAGATION_REQUIRES_NEW cannot be used with Hibernate, as Hibernate only supports one open transaction at a time. (To see the threads I am referring to, search for PROPAGATION_REQUIRES_NEW)

Does this mean that setting transaction attributes in Spring is impossible when we are using Hibernate? Is the only supported setting PROPAGARION_REQUIRED?

I am basically having a continous batch process that must commit data persisted through Hibernate in batches. This means that I must be able to commit periodically during the process. Is this possible at all when using Spring/Hibernate?

What alternatives do I have? This is really a problem in our project now, so I would really appreciate any help you have on this.

I am using JBoss 4.0, and have discovered that defining the datasource as <no-tx-datasource> instead of <local-tx-datasource> allows me to set PROPAGATION_REQUIRES_NEW, but it seems that this gives me problems with incomplete rollbacks, so I suppose this is a wrong track.

kidahl2
Apr 5th, 2005, 08:12 AM
I keep seeing references to this subject, but no-one really knows what the status Spring/Hibernate transaction management is. Could anyone elaborate on Martin Kerstens post in a different thread:
Sounds like a non-vital nested transaction for me. (Parent transaction can recover from a failing child transaction). The problem is that according to my information (may be old) that hibernate supports only the flat (single) transaction model. (this can be aged or wrong, I am not sure).

But if hibernate really only allows a single transaction, there is a theorem out there that might help you. According to this theorem it is possible to mimic non-vital nested transactions by using a look forward part. During this look forward part the business logic uses a sufficient test to determine if the roll back decision is made in the later step. This imposes a perfomance decrese and may affect cohesion in a bad way but it would be a solution.

Another idea would be using alternative transactions. You know following scenario:

MainTransaction {

beginTransaction (BOT),
write A,
write B,
do business stuff,
write C,
endTransaction (EOT);

}

AlternativeTransaction {
BOT;
write A,
write B,
write C,
EOT
}

So if the first transaction fails (gets rolled back), the alternative transaction is executed to recover from this transactional failure.

Both transaction look similar so encapuslating this transaction using a object hierarchy may be your friend here. (to avoid code duplication).


But maybe we are lucky and hibernate supports non-vital nested transactions now. But I lack this information... . :-(

Cheers,

Martin (Kersten)

Martin Kersten
Apr 5th, 2005, 08:19 AM
The original post can be found at http://forum.springframework.org/viewtopic.php?t=4544.

Martin Kersten
Apr 5th, 2005, 08:54 AM
I am basically having a continous batch process that must commit data persisted through Hibernate in batches. This means that I must be able to commit periodically during the process. Is this possible at all when using Spring/Hibernate?

Long transactions are usally a big problem. A transaction may depend on the results of a transaction still active (not-committed). (Visibility) If a long transaction runs, it might block other transactions (writing once), or in case of a failure may cause a cascading rollback problem. The problem is that a transaction depending on results of another transaction (by reading objects, which where written by another one) may not commit unless the other transaction it depends on is commited. In case of long concurrent transactions ever database acts normally quite differently. But mostly this acting isn't desirable anyway.


What alternatives do I have? This is really a problem in our project now, so I would really appreciate any help you have on this.

Can you tell us more about your batch process? What is its task? What is the Activity shema / diagram? What requirements?

Every different batch process has quite different requirements. When talking about transactions, we normally talk about ACID - atomacy, consistency, isolation, durability. If we want to emulate nested transactional behaviour, it nearly always comes down to weaken one or more of those four aspects of a transactional system - mostly A and I but I implemented a batch process half a year ago which also weakend C. (by bouncing a requirement back to the domain model -> A special test was introduced to detect inconsistencies).

Also using application level transactions (versioning for instance) is also an option often used by workflow systems for instance.

Another one is using commands to allow undos but this brings you in hell's kitchen, if the database crashes during such a batch process and a recovery is done.

I also know from a project (midsize) where the devs used two tables to support atomacy (comparable to the solution of the shadow memory concept of database systems or double buffering). Once a batch process finishes (they assured that all there batch processes where managed and synchronized) the system switched from using one table to using the table containing the updated informations (which is done by atomacy). Before a new batch process started, the second table was filled with the content of the primary one and got altered by the barch process(es). This has some pros and I guess they have a good story to tell you infront of a camping fire but I can't help, I don't like this one and it should only be applyable by single table datas - but noone can really stop you from altering more then one table - but hey finally it works! ;-)


Cheers,

Martin (Kersten)

kidahl2
Apr 5th, 2005, 11:35 AM
Thanks for your reply, Martin!

My batch process is triggeded every hour by the JBoss scheduler. On that event, the following processing must be done:

-Verify elements according to business rules, and set status on each verified element.
-For each verified element, do some processing and set a status.
-For each processed element with a given status, do some reporting and set status.
-...and several similar steps..

One "element" can be a huge amount of related data.

The idea is that as many elements as possible should be processed completely, or as far as possible. Only elements that fail for some reason should not be processed further, just given an appropriate (failure) status. This includes unchecked exceptions, database schema violations etc.

As you can see, I would like to commit status each time an element is processed and moved along the pipeline. And element data should be committed or rolled back depending on the result. A failure for some reason on one element should not cause a rollback of all work done.

All this seems impossible unless I can figure out how to start a new/nested transaction with Spring/Hibernate.

-Karl Ivar

Martin Kersten
Apr 5th, 2005, 11:59 AM
All this seems impossible unless I can figure out how to start a new/nested transaction with Spring/Hibernate.

This isn't much of a problem. First of all the Spring frameworks reference doc is your friend here. Chapter 7.3 explains you how you can create transactions programmatically. I would go for programmatical transactions here since transactions are now part of your implementation. Don't go with declarative transactions here. Someone will always forget to advice the appropriate methods. ;-D

Also I would use the requires-new transaction propagation strategy. You wouldn't want someone to surround your batch process by a transaction, which would alter the behaviour of your implementation (roll back all changes etc.).


One "element" can be a huge amount of related data.

Usually this ends with something like this:
1. Fetch all datas
2. Process datas
3. Store all changes

It maybe a good idea to use a read-only transaction during the first step, no transaction for the second step and finally a normal transaction (write) for the third part. (It depends on your requirements.)

Since your scenario is much likely to be a competitive one (depends on your requirements again), it might be a good idea to think about scheduling using tickets. You know just adding a timestamp for each element which express, when an element should next be updated/handled. Each batch process gets a element likely to be updated and increases the timestamp so that no other batch process will start to process the same element within a certain time (an hour for example). This can ease your life in situations of overload or distributed systems. Also it frees you from checking in the 3rd phase, if someone else had altered the element's data (since noone else would touch it in some scenarios - again it's depending on your project's requirements).


Cheers,

Martin (Kersten)

PS: If you have difficulties to get programmatic transaction management working, please feel free to send me a personal message.

kidahl2
Apr 5th, 2005, 12:27 PM
Thanks again for you feedback Martin,

I have tried programmatic transaction control using the PlatformTransactionManager with PROPAGATION_REQUIRES_NEW, but I got the same errormessage as when I used the AOP wrapper (Iīm not at work now, so I donīt have the error at hand). I did however not create a new Hibernate session, so the error might be related to this.

The Hibernate session is retrieved with SessionFactoryUtils.getSession(true), can this stil be used? I donīt see how I can supply a new Hibernate session here..

Martin Kersten
Apr 6th, 2005, 04:22 AM
I have tried programmatic transaction control using the PlatformTransactionManager with PROPAGATION_REQUIRES_NEW, but I got the same errormessage as when I used the AOP wrapper (Iīm not at work now, so I donīt have the error at hand). I did however not create a new Hibernate session, so the error might be related to this.

For such a batch process you need to ensure that your programmatic transactions are the topmost one. You should never run such a batch process inside of an existing transaction. For us it worked to run such a process in a sperated thread. Since sessions are stored in a thread local variable, it gives you a new session and your batch process can control the whole session, including the transactional behaviour.

If you still have difficults to set it up, just call me and I take a look at our batch process.


Cheers,

Martin (Kersten)

kidahl2
Apr 7th, 2005, 01:31 AM
Thanks for the advice, Martin.

I am using EJB stateless session beans as a thin layer above my Spring service beans. I believe the quickest way for me now would be to call the service bean that needs new transactions as an EJB. That would execute as a new thread and hopefully give me a new transaction as well..

kidahl2
Apr 7th, 2005, 07:55 AM
Well, I finally managed to ceate new transactions :D

The Spring managed bean with transaction attributes PROPAGATION_REQUIRES_NEW is now accessed as an EJB from the other spring managed beans (using a transparent Spring EJB wrapper). I guess this works as this then is the topmost transaction (as Martin pointed out in a previous post). All calls through this bean will execute in a new transaction.

I never quite understood why a sub-transaction cannot be opened from a direct springbean-to-springbean call, so anyone with a better idea are welcome to post below :)

spring007
May 4th, 2005, 02:42 PM
Hi,

I was going through the whole topic you had about starting a nested transaction in Spring framework. I am wondering if you have come across any "non-EJB" way to create a nested transaction using PROPAGATION_REQUIRES_NEW attribute so far. I was looking at a problem almost similar to yours and looking for a solution to periodically commit data. I spent almost a week to get this to work. At one time, I guessed I could create a nested transaction, but while committing to database, I got an error stating that " Could not deactiveate :synchronization is not active." I really don't know what it means. So, I did not really look any further thinking that you can never create a nested transaction in spring.

Your reply will be highly appreciated!!

Thanks.

kidahl2
May 5th, 2005, 03:56 AM
Unfortunately, I havenīt had time to look into alternatives. But I suspect that this is only a problem when using Hibernate with Spring, as Hibernate requires a new session for a new transaction. I believe this should have been solved by the Spring helper class that provides me with the Hibernate session (eg. is the transaction new => Then make new Hibernate session when someone asks for it ), but I do no know if that would have any side effects.

To solve this now, my service layer that controls the transactions also have to handle the hibernate sessions and provide a new/reuse an existing Hibernate session depending on the transaction settings. This would then have to be passed on to the persistence layer. But this violates the inversion of control principle a bit, so it seems messy to me.

spring007
May 5th, 2005, 09:01 AM
Thanks a lot for your reply.

This will atleast help me in seperating what I should do from what I should not as far as handling hibernate session with transactions is concerned.