Announcement Announcement Module
Collapse
No announcement yet.
The reason for nonthreadsafe access to Hibernate session Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • The reason for nonthreadsafe access to Hibernate session

    Hi all

    I'm maintaining a fairly standard Spring (2.0)/Hibernate(3.2.5) web application. We have DAOs configured as singletons, data service beans using TransactionProxyFactoryBean which are also configured as singletons and take the corresponding DAO as property.

    Now every now and then, I get an exception like
    Code:
    org.hibernate.AssertionFailure
    an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
    
    org.hibernate.AssertionFailure: possible nonthreadsafe access to session
    at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:84)	
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:234)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:146)	
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)	
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
    at net.umbrella.services.DataWriterImpl.evictSession(DataWriterImpl.java:1427)	
    at ...
    As this is (or so I believe) a threading issue, I'm having a hard time reproducing the error on my developement environment. So I'm trying to understand what Hibernate is trying to tell me here.

    Is a Hibernate session not thread-safe? I'm using direct access to the Hibernate API, not the DaoTemplate that Spring provides.

    Code:
    Session sess = this.sessionFactory.getCurrentSession();
    sess.delete(entity); 
    sess.flush();
    Do I need to move away from using singletons in the DAO/Service layer, do I need ThreadLocals somewhere or what?

    Thanks
    Simon

  • #2
    Just found that our data writer (DataWriterImpl) is configured as 'prototyp'. So the problem is probably the prototyp (thus multiple instances) trying to share one Hibernate session or something.

    I guess I'll have to move get the Hibernate part of the DataWriterImpl to behave as a singleton.

    Cheers
    Simon

    Comment


    • #3
      Hibernate Sessions aren't thread safe. There shouldn't be any problems with the configuration as you describe it, are you sharing Hibernate Sessions across threads? The currentSession approach should be fine as the transaction will be providing the threadLocal Session for you.

      Comment


      • #4
        Hi Karl

        I guess I must be sharing a Hibernate Session among threads, but where? Thinking out loud here:

        * 'datawriterImpl' is defined as a prototype and uses sessionFactory.getCurrentSession() to access the hibernate session
        * 'datawriter' is a TransactionProxyFactoryBean wrapping 'datawriterImpl', also set scope='prototype'
        * Then, there is a 'datawriterproxy' which gets a reference to the 'datawriter' bean, is also a prototyp, and has the Configurable annotation. I guess this one is here in order to get a transactional data writer
        * Finally, my code calls new DataWriterProxy().getDataWriter(); to get a data writer instance.

        So they're all working as prototyps. I see that the datawriterImpl gets its sessionFactory by calling getBean() on the application context instead of injection, which is ugly, but shouldn't break the code.

        What I also find is that, although I have a lot of calls to the Hibernate session, the breaking code is always:

        Code:
        public void evictSession() {
        	session.flush(); // this flush causes the exception!
        	session.clear();
        	session = null;
        }
        which is the only code which accesses the Hibernate session state variable directly. All other calls go via

        Code:
        public Session getSession() {
        	if ((session == null) || (!session.isOpen())) {
        		SessionFactory sessionFactory = (SessionFactory)this.factory.getBean("sessionFactory");
        		session = sessionFactory.getCurrentSession();
        	}
        	return session;
        }
        But still, if getCurrentSession() gives me a thread local session, and all my datawriter beans are prototypes, that session should stay local to my thread.

        Still stumped ...
        Simon

        Comment


        • #5
          Where and how do you fetch the datawriter intances?

          Comment


          • #6
            Originally posted by dejanp View Post
            Where and how do you fetch the datawriter intances?
            I always do a

            Code:
            new DataWriterProxy().getDataWriter();
            The DataWriterProxy has a configurable annotation, so Spring injects a data writer instance. As the datawriter bean is defined as prototyp, I'd expect that to be a new instance each time. I believe I once tested even that.

            Thanks
            Simon

            Comment


            • #7
              What I find odd is that the code is structured like this:

              Code:
              try {
                a lot of stuff here
              }
              finally {
                 evictSession();
              }
              // end of the method
              where the evictSession does flushes and clears the hibernate session (as posted earlier). I going to remove that, as the transaction will flush and clear the session anyway. But does that make sense to anyone: if the inner code throws an exception, rendering the hibernate session useless, and in the finally-block the session is flushed, could the "possible nonthreadsafe access" exception occur?

              Thanks
              Simon

              Comment

              Working...
              X