-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Session spanning multiple JTA transactions
PostPosted: Mon Apr 11, 2005 3:46 am 
Regular
Regular

Joined: Thu Nov 13, 2003 2:55 am
Posts: 71
Location: Sweden
[I never got a real answer to this in the Hibernate3 forum, but hopefully some "pre version 3" users can provide the solution]

If a Hibernate Session spans several JTA transactions (or in any other way connects to the database before a JTA transaction), the connection Hibernate uses will not be correctly enlisted with the JTA transaction, since it is cached inside the Session.

Code:
    SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
    Session session = sessionFactory.openSession();

    UserTransaction ut = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
    ut.begin(); // Start TX 1
    // Read object from database, will get Connection from
    // XADataSource and thus enlist Connection with JTA transaction
    ut.commit(); // Commit TX 1
   
    ...

    ut.begin(); // Start TX 2
    session.beginTransaction(); // Add cache synchronization to JTA transaction
    // Read object from database, will REUSE CONNECTION and thus the Connection will not be a JTA resource
    // Update object
    session.flush();
    session.update(...);
    ut.rollback(); // Rollback TX 2. This DOES NOT WORK, since the Connection is not managed by JTA
   
    session.close();

After much testing and debugging I concluded this requires manual disconnect() and reconnect() (or close() and reopen) of the session, which to me seems like a bug in Hibernate (since "session-per-request-with-multiple-transactions" is a suggested pattern; http://hibernate.org/168.html). Though I was told it was not - it is a "simple user error".

So could somebody please tell me how this is supposed to be solved???

[Original discussion with workarounds can be found here: http://forum.hibernate.org/viewtopic.php?t=940768]


Top
 Profile  
 
 Post subject: So...
PostPosted: Tue Apr 12, 2005 2:15 pm 
Regular
Regular

Joined: Thu Nov 13, 2003 2:55 am
Posts: 71
Location: Sweden
So... I've been reading up a bit.

It seems that using Hibernate - at least in a JTA environment - is not by far as homogenous and straighforward as I had previously believed. There are tips on Synchronizations, special ThreadLocal factories and lots of other ideas to run things as smoothly as possible, but there seems to be no such thing as "transparent" Hibernate integration. You have to do some manual coding to get Hibernate hooked up with JTA, apart from the most trivial cases. So it seems I have to invent something that suits our needs.

Though I still think that the patch I provided to JIRA (http://opensource.atlassian.com/projects/hibernate/browse/HHH-322) would make life a lot easier, and remove some of the JTA + Hibernate gotchas (or at least one really big).


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 12, 2005 3:21 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Not understanding what "manual coding" means here.

In specific to your JIRA case, in JTA environment with the auto_close_session setting enabled in H3, Hibernate will be very aggressive about releasing JDBC connections (it basically gets a connection, uses it, and immediately releases it back to the DS); essentially it disconnects/reconnects in a (over-)simplified view.


Top
 Profile  
 
 Post subject: Not quite
PostPosted: Tue Apr 12, 2005 3:50 pm 
Regular
Regular

Joined: Thu Nov 13, 2003 2:55 am
Posts: 71
Location: Sweden
steve wrote:
Not understanding what "manual coding" means here.

I meant that you need to add explicit Session disconnect() / reconnect() etc (depending on what you want to do).

steve wrote:
In specific to your JIRA case, in JTA environment with the auto_close_session setting enabled in H3, Hibernate will be very aggressive about releasing JDBC connections (it basically gets a connection, uses it, and immediately releases it back to the DS); essentially it disconnects/reconnects in a (over-)simplified view.

Well, since auto_close_session requires that a new Session is opened for the next JTA transaction, the problem is implicitly solved, but this requires a special ThreadLocal Session factory, that creates a new Session if the one currently bound to the Thread has been closed (this was part of "manual coding"). I had considered this approach...

BUT...this does not work if the Session is used outside any JTA Transaction (i.e. in auto-commit transactions) before any JTA Transaction is started.

I.e. this code does not work (but does with my patch...):
Code:
    SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
    Session session = sessionFactory.openSession();

    // Read object from database, will get Connection from
    // XADataSource BUT NOT enlist Connection with JTA
   
    ...

    UserTransaction ut = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
    ut.begin(); // Start TX
    session.beginTransaction(); // Add cache synchronization to JTA transaction
    // Read object from database, will REUSE CONNECTION and thus the Connection will not be a JTA resource
    // Update object
    session.flush();
    session.update(...);
    ut.rollback(); // Rollback TX. This DOES NOT WORK, since the Connection is not managed by JTA


Top
 Profile  
 
 Post subject: P.S.
PostPosted: Tue Apr 12, 2005 3:57 pm 
Regular
Regular

Joined: Thu Nov 13, 2003 2:55 am
Posts: 71
Location: Sweden
steve wrote:
In specific to your JIRA case, in JTA environment with the auto_close_session setting enabled in H3, Hibernate will be very aggressive about releasing JDBC connections (it basically gets a connection, uses it, and immediately releases it back to the DS)

(P.S. I was unable to verify this in the code. It seems to me the Connection is only returned to the DataSource at transaction commit/rollback.)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 12, 2005 4:13 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Quote:
(P.S. I was unable to verify this in the code. It seems to me the Connection is only returned to the DataSource at transaction commit/rollback.)

Check CVS. This will be in 3.0.1


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 12, 2005 4:19 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Quote:
// Read object from database, will get Connection from
// XADataSource BUT NOT enlist Connection with JTA

This does work with the new code because the connection (i.e. the JCA handle) is returned to the DS everytime and re-retreived everytime.

Yes it is true that the session would also get closed. We actually discussed having a seperate config parameter here to define the eager connection releasing, but could not come up with a compelling use-case for it. This might be one. Enter a JIRA case for this enhancement request and I'll think about whether it really makes sense.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 12, 2005 4:42 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
BTW, you realize this is dangerous right?

If a session flushes in one of the "not the final" transactions, you've just lost atomicty of the overall transaction (a session is intended as a *transactional* thing).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 14, 2005 2:11 am 
Regular
Regular

Joined: Thu Nov 13, 2003 2:55 am
Posts: 71
Location: Sweden
[Forgot to post this yesterday...]

steve wrote:
Check CVS. This will be in 3.0.1

Indeed, the code in the CVS head - without my patch - handles my scenario when using auto_close_session.

steve wrote:
BTW, you realize this is dangerous right?

If a session flushes in one of the "not the final" transactions, you've just lost atomicty of the overall transaction (a session is intended as a *transactional* thing).

Depends on what you mean by "dangerous". If I have multiple transactions within a request/Session, I want each transaction to be ACID, though I do not care about the atomicity of the request/Session. I.e. if I have two transactions and an exception is thrown halfway within the second one, surely I still want the changes made by the first one to be persisted. (If I would not, I would use a single transaction!)

Here are some example use-cases you might want to consider when thinking about whether discussed changes makes sense:

In several places in our system, we want to update a log table outside the JTA transaction, so that there will be a log record both for sucessfull and failing updates. That is:
Code:
[Create log record]
try {
  userTransaction.begin();
  [Try to perform update]
  userTransaction.commit();
  [Mark log record as successful]
}
catch(Exception ex) {
  userTransaction.rollback();
  [Mark log record as failure]
  throw ex;
}
finally {
  [Update log record]
}

We also have a few scenarios where it would be handy (or at least the most straightforward) to have a Session span several JTA transactions; although none of them should be necessary. Here are two examples:
Example 1: Read data for multiple updates. If one update fails, continue with the next one.
Code:
[Read data for updates]
[Look in database for records to update]
for(Foo f: foos) {
  try {
    userTransaction.begin();
    [Try to update f]
    userTransaction.commit();
  }
  catch(Exception ex) {
    userTransaction.rollback();
    [Log error]
  }
}
(Truth be told, the loop is actually a loop across multiple databases/schemas/catalogs/namespaces, where the same table would be updated in each database/schema/catalog/namespace. So to stay clear of caching issues, separate Session might be preferrable)

Example 2: Within a single request, perform one mandatory and one "voluntary" update
Code:
try {
  userTransaction.begin();
  [Perform mandatory update]
  userTransaction.commit();
}
catch(Exception ex) {
  userTransaction.rollback();
  throw ex;
}

try {
  userTransaction.begin();
  [Perform "voluntary" update]
  userTransaction.commit();
}
catch(Exception ex) {
  userTransaction.rollback();
  [Log error]
}


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.