Announcement Announcement Module
Collapse
No announcement yet.
How to check there already is Hibernate session for thread? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to check there already is Hibernate session for thread?

    I'd like to be able to know if there already is a Hibernate session assigned for current thread or not. Currently, if I do getSession() for SessionFactoryUtils when there is no session bound to thread, it will either create a new session or throw InvalidStateException, depending on whether we allow creation of new sessions or not.

    What I would like to do is write code that could either participate already existing session (just access the data, no flush() or close()), or if there isn't one, create it locally, use it, and finally do flush() and close(). In other words, if there is session, let it be managed outside my code and if not, create and manage session locally.

    This way, I could reuse the code as 'standalone' or part of larger process, possibly part of a transaction.

    Any ideas?

  • #2
    Sounds like you want the PROPAGATION_SUPPORTS behaviour. See org.springframework.transaction.TransactionDefinit ion. You can use this declaratively.

    Comment


    • #3
      Not really, or I didn't understand it correctly.

      My key point was not about transactions. I just would like to be able to use same piece of code both when I have declarative transactions (and thus Hibernate sessions) and when there are no thread bound sessions at all.

      I would execute my code using a callback, using code like this:

      Code:
          public static void doInSession(HibernateSessionWork work) {
              if (work == null)
                  throw new IllegalArgumentException("Work can not be null");
      
              try {
                  Session session = HibernateUtils.sessionFactory.openSession();
                  if (session != null) {
                      try {
                          work.doInSession(session);
                          session.flush();
                      } finally {
                          session.close();
                      }
                  } else
                      throw new RuntimeException("Could not get Hibernate session");
              } catch (HibernateException e) {
                  throw SessionFactoryUtils.convertHibernateAccessException(e);
              }
          }
      Code above only manages sessions locally. What I would like to do is use thread local session if it's available, but I can't do that because SessionFactoryUtils doesn't allow me to just check whether there is a session bound to current thread or not. Ofcourse, if there is a session bound to the thread, I wouldn't do flush() nor close().

      Comment


      • #4
        You can use the same code that SessionFactoryUtils does, to see if there is a bound session:

        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sess ionFactory);

        If sessionHolder is non-null, there is a bound session...

        Probably best to put this in a wrapper method, if you use it from more than one place.

        Comment


        • #5
          Thanks! This seems to be what I was looking for.

          Comment


          • #6
            Ok, here's my current wrapper method:

            Code:
                public static void executeInSession(HibernateSessionWork work) {
                    if (work == null)
                        throw new IllegalArgumentException("Work can not be null");
            
                    try {
                        //use thread bound session if it's available
                        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
                        boolean localSession = (sessionHolder == null);
                        Session session = SessionFactoryUtils.getSession(HibernateUtils.sessionFactory, localSession);
            
                        //if we created session locally, we need to bind it to thread
                        if (localSession) {
                            sessionHolder = new SessionHolder(session);
                            TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
                        }
            
                        if (session != null) {
                            try {
                                //pass session factory to work
                                work.setSessionFactory(HibernateUtils.sessionFactory);
                                //do stuff that uses session
                                work.execute();
                                if (localSession)
                                    session.flush();
                            } finally {
                                //cleanup
                                work.setSessionFactory(null);
            
                                if (localSession) {
                                    session.close();
                                    TransactionSynchronizationManager.unbindResource(sessionFactory);
                                }
                            }
                        } else
                            throw new RuntimeException("Could not get Hibernate session");
                    } catch (HibernateException e) {
                        throw SessionFactoryUtils.convertHibernateAccessException(e);
                    }
                }
            And the skeleton class for work is:

            Code:
            public abstract class HibernateSessionWork {
                private static final Log logger = Logging.getLog(HibernateSessionWork.class);
            
                private SessionFactory sessionFactory;
            
                void setSessionFactory(SessionFactory sessionFactory) {
                    this.sessionFactory = sessionFactory;
                }
            
                /**
                 * Get an instance of HibernateTemplate, bound to current session.
                 *
                 * @return
                 */
                public final HibernateTemplate getHibernateTemplate() {
                    return new HibernateTemplate(this.sessionFactory, false);
                }
            
                /**
                 * Get the current Hibernate session.
                 *
                 * @return
                 */
                public final Session getSession() {
                    SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
                    return sessionHolder.getSession();
                }
            
                /**
                 * To be used with <code>HibernateUtils.executeInSession&#40;&#41;</code>. Encapsulate
                 * a piece of work that requires a Hibernate session, without actually
                 * doing session management.
                 *
                 * To actually access Hibernate, implementation should get instances of Session and 
                 * HibernateTemplate using <code>getSession&#40;&#41;</code> and <code>getHibernateTemplate</code>.
                 *
                 * If this method throws an exception, session will NOT be flushed to database
                 * &#40;but it will be closed, still&#41;.
                 */
                public abstract void execute&#40;&#41;;
            &#125;
            Does this seem to be right? I'm a bit concerned about the lines that call TransactionSychronizationManager - if sessions are managed locally, there are no transactions and calling TSM sounds scary to me.. Am I potentially breaking something because of (un)bindResource() calls?

            Comment


            • #7
              On a cursory look, your code looks fine. There's no issue with using TransactionSychronizationManager, ultimately, it's just synchronizes thread binding of this and other resources...

              Comment

              Working...
              X