Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to break a Hibernate session?

In the Hibernate reference, it is stated several times that

All exceptions thrown by Hibernate are fatal. This means you have to roll back the database transaction and close the current Session. You aren’t allowed to continue working with a Session that threw an exception.

One of our legacy apps uses a single session to update/insert many records from files into a DB table. Each record update/insert is done in a separate transaction, which is then committed (or rolled back in case an error occurred). Then for the next record a new transaction is opened, and so on. But the same session is used throughout the whole process, even if a HibernateException was caught in the middle of processing. We are using Oracle 9i btw with Hibernate 3.24.sp1 on JBoss 4.2.

Reading the above in the book, I realized that this design may fail. So I refactored the app to use a separate session for each record update. In a unit test with a mock session factory, I can verify that it is now requesting a new session for each record update. So far, so good.

However, we found no way to reproduce the session failure while testing the whole app (would this be a stress test btw, or ...?). We thought of shutting down the listener of the DB but we realized that the app is keeping a bunch of connections open to the DB, and the listener would not affect those connections. (This is a web app, activated once every night by a scheduler, but it can also be activated via the browser.) Then we tried to kill some of those connections in the DB while the app was processing updates - this resulted in some failed updates, but then the app happily continued updating the rest of the records. Apparently Hibernate is clever enough to reopen broken connections under the hood without breaking the whole session.

So this might not be a critical issue after all, as our app seems to be robust enough even in its original form. However, the issue keeps bugging me. I would like to know:

  1. Under what circumstances does the Hibernate session really become unusable after a HibernateException was thrown (Update: and what are the symptoms)?
  2. How to reproduce this in a test (Update: preferably in integration, rather than unit test)?
  3. (What's the proper term for such a test?)
like image 727
Péter Török Avatar asked Apr 02 '10 09:04

Péter Török


People also ask

How do you close a session in Hibernate?

Cancel the execution of the current query. Completely clear the session. End the session by releasing the JDBC connection and cleaning up.

Do I need to close session in Hibernate?

Hibernate SessionFactory openSession() method always opens a new session. We should close this session object once we are done with all the database operations. We should open a new session for each request in multi-threaded environment.

What is session flush in Hibernate?

Flushing the session forces Hibernate to synchronize the in-memory state of the Session with the database (i.e. to write changes to the database). By default, Hibernate will flush changes automatically for you: before some query executions. when a transaction is committed.

What happens if Hibernate session is not closed?

When you don't close your Hibernate sessions and therefore do not release JDBC connections, you have what is typically called Connection leak. So, after a number of requests (depending on the size of your connection pool) the server will not be able to acquire a connection to respond your request.


2 Answers

Under what circumstances does the Hibernate session really become unusable after a HibernateException was thrown?

Interesting question. I'm pretty sure I've seen cases where the session was still "working" after such an exception. The problem is maybe that this isn't guaranteed. I'll need to dig this a bit more.

Update: Quoting this message from an Hibernate developer in the Hibernate forums:

The hibernate session should be thrown away when any exception occurs. It can be left in a inconsistent state relative to the database. This is not the case with a read only session since it does not employ any session level caching. Alternatively, you could clear the session level cache when an exception occurs (but I don't personally like this approach as it is not a clean solution and it can result in future issues and problems).

So the problem is not really if the Session is "technically usable" or not, the problem is that it might not behave as expected. And you clearly don't want that.

How to reproduce this in a test?

You could deliberately violate a condition causing a subclass to be thrown, for example by violating an integrity constraint to get a ConstraintViolationException. What about something like that:

Session session = HibernateUtil.getCurrentSession();

try {
    session.beginTransaction();    
    // execute some code that violates a unique constraint
    ...
    session.getTransaction().commit();  
} catch (HibernateException e) {
    session.getTransaction().rollback();
}

// keep using the session
try {
    session.beginTransaction();    
    ...
    session.getTransaction().commit();  
} catch (HibernateException e) {
    session.getTransaction().rollback();
}
// do we reach this point?

But I'm not sure this will prove anything. I mean, if the session is still working, we can only conclude for this particular case.

Update: Forget my suggestion, it won't prove anything. And actually, I think that it would be extremely complicated (technically and functionally) to prove that things are working as expected (and that could still be an "accident") after a HibernateException. So, instead of trying to prove anything, I guess that the right thing to do is to follow the recommendation (or the suggested alternative but I would just request a new Session and close it for every transaction, exception thrown or not).

What's the proper term for such a test?

Integration Test? Robustness Test?

like image 106
Pascal Thivent Avatar answered Sep 19 '22 16:09

Pascal Thivent


Interesting one. The best way to tell is to go and take a look at Hibernate's sources. As far as I get it, the Session does not carry much state around, except for the following stuff:

  • first-level cache (see StatefulPersistenceContext) -- it might be actually broken after transaction failure;
  • actions queue (see ActionQueue) -- seem to be okay if you do manual flushing;
  • event listeners -- their instances are aware of session (see EventSource) and, as you might have noticed, the JDBC exceptions are always caused by flushing (DefaultFlushEventListener, to be more precise), but a quick glance at AbstractFlushingEventListener shows that they actually work with session's JDBCContext and ActionQueue.

And the last thing, rollback of transaction itself does not interfere with Session state in any way, see JDBCTransaction.

So as far as I've played around, the Session actually survives a faulty transaction, except that first-level cache might be slightly inaccurate; to get around this you may prefer to call session.refresh or session.load to explicitly resynchronize states.

EDIT: the quickest way to test JDBC exceptions is probably session.createSQLQuery("something offensive to SQL parser").executeUpdate() -- you'll get a perfect SQLGrammarException -- and a broken transaction =)

like image 35
BorisOkunskiy Avatar answered Sep 19 '22 16:09

BorisOkunskiy