The situation is more or less like this:
1. An user presses a button.
2. HTTP Request goes to Filter Chain.
3. One of our Filters is CustomizedOpenSessionInViewFilter in lams_common/.../util.
This filter opens a single session for the whole Request. The way we use it roughly reminds this pattern:
http://www.hibernate.org/43.html
Our filter is "custom" as it set session FlushMode to AUTO, instead of the original NEVER.
4. Request goes to an Action method, for example to save a tool's content.
5. After some validation, we get to a transactional method, like the one that saves the content. Such method is declared in Spring context file as transactional (proxy, PROPAGATION_REQUIRED etc.)
6. "Proxying" of service methods works fine. The method call goes through Spring first wrapper, rather than straight to tool service.
At this moment Spring creates a new transaction. This transaction wraps all Hibernate statements in the given service method call. This works, but
SPRING DOES NOT BEGIN THE TRANSACTION
like it should.
I checked that transaction does exist and its name is equal to service method's (so Dapeng Ni was slightly wrong when he wrote "the following methods will become non-transactional methods"). The transaction was not started - connection was still set to autocommit mode (and it usually is not, while in transaction). I started the transaction myself and only then autocommit mode was set to false.
Not only that, the transaction will also NOT ROLL BACK. Of course if it was not started, it should not roll back. But even after I started it myself and then thrown an exception (which should cause a roll back) or set the current transaction as ROLLBACK_ONLY (which should do a roll back at session close), changes in DB would not be reverted.
Also, please notice one thing. Sometimes in the code we are using Flush method in Hibernate. Also, like I said in point 3, our FlushMode is set to AUTO, so we allow Hibernate to Flush in some situations. Flush puts the changes into DB. But even if they are already in the DB, they *can be reverted by a transaction roll back*.
This explains the behaviour described by Fiona:
"The records are written to the database as soon as they are inserted in Hibernate. The method is wrapped up in a Spring transaction, but there is something wrong with Hibernate/JDBC."
There is nothing wrong with that. Hibernate Flushes immediately after creating new objects - that's OK. If we make changes to these objects, they are not reflected in the DB until we Flush - that's also OK. But our problem is that we can not revert those changes - *roll back does not work correctly*.
This Flush policy explains also why sometimes things "work". The next step for Request is:
7. Session is closed. If there were no errors, it is closed by our CustomizedOpenSessionInViewFilter. We have a Flush there, just before close. All pending SQL statements are executed. This looks like a transaction commit, but it is NOT a commit, because there is no transaction running!
If there were errors, the session is also closed, but by an internal Spring method. This method does not Flush. It looks like a roll back, but it is NOT a roll back! The pending statements are simply discarded and never executed in DB. *But whatever was Flushed earlier, stays in the DB* That's a major difference between Flush and commit.
Right now we base on Flushes. We should more depend on transactions.
Also, the behaviour described by Fiona:
"So when a later service level transaction fails, it triggers a rollback on the database, which then rolls back all the changes for the previous service level transactions, so in practice the whole lot does roll back."
seems strange. It might suit us, but it is wrong. Only the changes made by the given transaction should be rolled back, not all the previous... If it's happening, then it's more a side effect than desired behaviour. And this means that there can be other side effects too.
Our situation is very similar to this one:
http://forum.springsource.org/showthread.php?t=15486
but no matter what I do with these settings:
<prop key="hibernate.connection.release_mode">auto</prop>
<prop key="hibernate.current_session_context_class">jta</prop>
<prop key="hibernate.transaction.auto_close_session">true</prop>
which can be found also in
http://forum.springsource.org/showthread.php?t=63532
I can't get a good effect. I think our situation is slightly different. It is not about JDBC connections, it is about transactions themselves.
Possible reasons are:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-1062
http://opensource.atlassian.com/projects/hibernate/browse/HHH-1410
or our
LDEV-2201
Whatever is the reason, it seems that our OpenSessionInViewFilter/JTA/Hibernate/JBoss combination causes some serious problems. I will come back to this JIRA after resolving
LDEV-2201 and
LDEV-2071, as they will change the situation and the problem may become solved as a side effect. On the other hand some new problems may appear, so there is no point in solving this issue right now.
In Forum, when you save a design (in authoring), the createRootTopic() method isn't behaving correctly. The records are written to the database as soon as they are inserted in Hibernate. The method is wrapped up in a Spring transaction, but there is something wrong with Hibernate/JDBC.
Yes if the same method is called from monitoring (during the creation of a lesson) then it works fine - the database isn't updated until the transaction ends.
Normally within a transaction, the first db call triggers "set autocommit=0 ", then does the queries/updates. At the end of the transaction, it does "set autocommit=1". This can be seen in the mysql log by adding the entry
log="C:/temp/mysql.log"
to mysql.ini.
But when it runs within Forum, the "set autocommit=0" doesn't happen.