-->
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.  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: HibernateUtil: closeSession() and commitTransaction()
PostPosted: Thu Sep 01, 2005 6:58 am 
Beginner
Beginner

Joined: Sat Sep 04, 2004 7:07 am
Posts: 20
Location: Helsinki, Finland
Using HibernateUtil it's possible to closeSession and not to commitTransaction, which causes later net.sf.hibernate.HibernateException: Session is closed. Could this kind of misuse (?) be prevented by including
Code:
transactionCommit();
to the method
Code:
HibernateUtil.closeSession(){...}
? Or at least add
Code:
threadTransaction.set(null);
to
Code:
HibernateUtil.closeSession(){...}
? Or is there some situation in which it makes sense to close the session but keep the transaction? The HibernateUtil.java is such a valuable piece of Java code that I'm quite hesitant to make any (unauthorized) own corrections or improvements.

_________________
Risto


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 7:03 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
You can make whatever changes you like, but I wouldn't do what you proposed. I'd probably check if the transaction is still uncommitted in the close() method and throw an exception. It's certainly an error to do this.


Top
 Profile  
 
 Post subject: using HibernateUtil under CMT and without
PostPosted: Thu Sep 01, 2005 6:59 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Hello,

Would it be possible to have HibernateUtil written in such a way that
it could be used both under CMT (Container-Managed-Transctions) and
without?

HibernateUtil is used by DAOs and people might want to use DAO
in CMT or in standalone apps (so they will commit transaction in their code).

I see that there is no call to sessionFactory.getCurrentSession() in current
HibernateUtil. AFAIK this is the method which has to be used under CMT
together with something like

Code:
<property name="transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</property>
<property name="transaction.factory.class">org.hibernate.transaction.CMTTransactionFactory</property>


in hibernate-config.xml

DAOs call HibernateUtil.getSession() to get the session, and with CMT
it should return current Session and create a new only if it doesn't exit.

I believe that both DAOs code and HibernateUtil code should be written
in such way that it is same whether it is used in CMT or client
manages the transaction.


Thanks,
--MG

PS: Here is HibernateUtil I'm using (but it is far from perfect)
Code:
import javax.naming.InitialContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

   private static Log log = LogFactory.getLog(HibernateUtil.class);

   private static final SessionFactory sessionFactory;
   private static final boolean SessionFactoryFromJNDI = false;

   private static final String HIBERNATE_CONFIG = "hibernate.cfg.xml";
   private static final String HIBERNATE_JBOSS_CONFIG = "/META-INF/hibernate-jboss.cfg.xml";

   private static boolean useCMT = true;

   static {
      try {
         // Create the SessionFactory
         if (SessionFactoryFromJNDI)
            sessionFactory = (SessionFactory) new InitialContext()
                  .lookup("SessionFactoryJNDI");
         else {
            String configName = HIBERNATE_JBOSS_CONFIG;
            String standalone = System.getenv("HIBERNATE_STANDALONE");
            if ((standalone != null) && (standalone.equalsIgnoreCase("YES"))) {
               configName = HIBERNATE_CONFIG;
               useCMT = false;
               System.err.println("HIBERNATE_STANDALONE=" + standalone);
            }
            Configuration config = new Configuration().configure(configName);
            sessionFactory = config.buildSessionFactory();
         }
      } catch (Throwable ex) {
         // Make sure the exception is logged, as it might be swallowed
         ex.printStackTrace(System.err);
         log.error("Initial SessionFactory creation failed.", ex);
         log.error(ex.getStackTrace());
         throw new ExceptionInInitializerError(ex);
      }
   }

   public static final ThreadLocal<Session> session = new ThreadLocal<Session>();

   public static Session currentSession() {
      Session s = null;
      if (useCMT) {
         s = sessionFactory.getCurrentSession();
         if (s == null) {
            s = sessionFactory.openSession();
         }         
      }
      else {
         s = (Session) session.get();
         // Open a new Session, if this Thread has none yet
         if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);
         }         
      }
      return s;
   }

   public static void closeSession() {
      if (session != null) {
         Session s = (Session) session.get();
         if (s != null)
            s.close();
         session.set(null);         
      }
   }
}



Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 7:07 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Indeed it would be very trivial to write a portable HibernateUtil. It's also documented here: http://www.hibernate.org/42.html


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 8:04 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Christian,

I hope you will put the portable HibernateUtil in the next edition of the book.
So far it only has a comment stating that it has to be changed
(to call getSessionFactory().getCurrentSession()) for CMT.

Code:
    public static Session getSession() {
         // With CMT, this should return getSessionFactory().getCurrentSession() and do nothing else
        Session s = (Session) threadSession.get();
        if (s == null) {
            log.debug("Opening new Session for this thread.");
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
        return s;
    }


It seems openSession() (please, correct me if I wrong, I've looked at org/hibernate/impl/SessionFactoryImpl.java) always
creates a new session.

Thanks,
--MG


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 8:11 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Everything else is a no-op, as commented. In fact transaction demarcation should never be called with CMT and the only "porting" that would make sense would be transaction-less DAOs with a higher level transaction demarcation (interceptor, servlet filter, etc.) in use in a non-managed environment. The job of the transaction interceptor is then done by CMT in a managed environment.

You don't need to change anything in the regular HibernateUtil class if you simply want to switch from non-managed to BMT environment.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 8:21 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Christian,

With CTM

Code:
    public static Session getSession() {
        Session s = (Session) threadSession.get();
        if (s == null) {
            log.debug("Opening new Session for this thread.");
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
        return s;
    }


would have to be replaced by

Code:
    public static Session getSession() {
       return getSessionFactory().getCurrentSession();
    }


What I was looking is implementation of getSession()
which doesn't have to change at all.

Might be I'm missing something.

Thanks a lot,
--MG


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 8:36 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
That's what the comment says, right? So yes.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 8:48 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Than it leads back to original question :)
How to implement getSession()
which doesn't have to change at all (under CMT, client managed transaction,
etc) to make HibernateUtil truly "portable".

I'm sorry for bugging you with this. I'm just looking for some improvements
in HibernateUtil.

Thanks,
--MG


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 8:55 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Use a system property. Read a configuration file. Try to figure out if you run in an application server (JNDI lookup, etc.). Use whatever switch you like. I'd just deploy the HibernateUtil I need in each scenario and don't bother with any of this.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 8:59 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Btw, you can read custom system properties or hibernate.properties with configuration.getProperties(). You can even put it into hibernate.cfg.xml if you call configuration.configure() first. Just add a "hibernateutil.use_thread_local" option to that if you want to keep things simple.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 9:15 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Thanks, this gives some ideas.

It would have been nice if SessionFactory had a method something like
getTransactionManagementType() or so.

It does have knowledge about TransactionFactory,
so it should be aware of what transaction management it
is running under without adding additional properties to
hibernate.cfg.xml, or hibernate.properties.

Many people have problems get these files right even with
the few variables it requires now. But adding a new variable there would work for me. Thanks.

--MG


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 9:23 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
You can read both the TransactionFactory name and the SessionFactory name (enabling or disabling JNDI) from configuration.getProperties().


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 01, 2005 9:35 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Untested:

Code:
package org.hibernate.ce.auction.persistence;

import org.hibernate.*;
import org.hibernate.util.PropertiesHelper;
import org.hibernate.cfg.*;
import org.apache.commons.logging.*;


/**
* Basic Hibernate helper class, handles SessionFactory, Session and Transaction.
* <p>
* Uses a static initializer to read startup options and initialize
* <tt>Configuration</tt> and <tt>SessionFactory</tt>
* <p>
* This class tries to figure out if either ThreadLocal handling of the
* <tt>Session</tt> and <tt>Transaction</tt> should be used, or if CMT is used.
* In the latter case transaction/session handling methods are disabled and the
* container does the job with Hibernate's automatic JTA binding. To keep your
* DAOs free from any of this, just call <tt>HibernateUtil.getSession()</tt> in
* the constructor of each DAO and use either client transactions/BMT or CMT
* to set transaction and session boundaries.
* <p>
* If you want to assign a global interceptor, set its fully qualified
* classname with the system (or hibernate.properties) property
* <tt>hibernateutil.interceptor</tt>. It will be loaded and instantiated
* on static initialization of HibernateUtil, and it has to have a
* no-argument constructor. You can call <tt>getInterceptor()</tt> if
* you need to provide settings before using the interceptor.
*
* @author christian@hibernate.org
*/
public class HibernateUtil {

   private static Log log = LogFactory.getLog(HibernateUtil.class);

   private static Configuration configuration;
   private static SessionFactory sessionFactory;
   private static ThreadLocal threadSession = new ThreadLocal();
   private static ThreadLocal threadTransaction = new ThreadLocal();

    private static boolean useJNDIBinding = false;
    private static boolean useThreadLocal = true;

   // Create the initial SessionFactory from the default configuration files
   static {
      try {
            // Replace with Configuration() if you don't use annotations
         configuration = new AnnotationConfiguration();

            // Read not only hibernate.properties, but also hibernate.cfg.xml
            configuration.configure();

            // Assign a global, user-defined interceptor with no-arg constructor
            String interceptorName = PropertiesHelper.getString("hibernateutil.interceptor_class",
                                                                configuration.getProperties(),
                                                                null);
            if (interceptorName != null) {
                Class interceptorClass =
                        HibernateUtil.class.getClassLoader().loadClass(interceptorName);
                Interceptor interceptor = (Interceptor)interceptorClass.newInstance();
                configuration.setInterceptor(interceptor);
            }

            // Enable JNDI binding code if the SessionFactory has a name
            if (configuration.getProperty(Environment.SESSION_FACTORY_NAME) != null)
                useJNDIBinding = true;
           
            // Disable ThreadLocal Session/Transaction handling if CMT is used
            if ("org.hibernate.transaction.CMTTransactionFactory"
                 .equals( configuration.getProperty(Environment.TRANSACTION_STRATEGY) ) )
                useThreadLocal = false;

            if (useJNDIBinding) {
                // Let Hibernate bind it to JNDI
                configuration.buildSessionFactory();
            } else {
                // or use static variable handling
                sessionFactory = configuration.buildSessionFactory();
            }

      } catch (Throwable ex) {
         // We have to catch Throwable, otherwise we will miss
         // NoClassDefFoundError and other subclasses of Error
         log.error("Building SessionFactory failed.", ex);
         throw new ExceptionInInitializerError(ex);
      }
   }

    /**
     * Returns the original Hibernate configuration.
     *
     * @return Configuration
     */
    public static Configuration getConfiguration() {
        return configuration;
    }

   /**
    * Returns the SessionFactory used for this static class.
    *
    * @return SessionFactory
    */
   public static SessionFactory getSessionFactory() {
      if (useJNDIBinding) {
            SessionFactory sessions = null;
            try {
                log.debug("Looking up SessionFactory in JNDI.");
                javax.naming.Context ctx = new javax.naming.InitialContext();
                String jndiName = "java:hibernate/HibernateFactory";
                sessions = (SessionFactory)ctx.lookup(jndiName);
            } catch (javax.naming.NamingException ex) {
                throw new RuntimeException(ex);
            }
            return sessions;
        }
        if (sessionFactory == null)
            throw new IllegalStateException("Hibernate has been shut down.");
        return sessionFactory;
   }

    /**
     * Closes the current SessionFactory and releases all resources.
     * <p>
     * The only other method that can be called on HibernateUtil
     * after this one is rebuildSessionFactory(Configuration).
     * Note that this method should only be used with static SessionFactory
     * management, not with JNDI or any other external registry.
     */
    public static void shutdown() {
        log.debug("Shutting down Hibernate.");
        // Close caches and connection pools
        getSessionFactory().close();

        // Clear static variables
        configuration = null;
        sessionFactory = null;

        // Clear ThreadLocal variables
        threadSession.set(null);
        threadTransaction.set(null);
    }


   /**
    * Rebuild the SessionFactory with the static Configuration.
    * <p>
     * This method also closes the old SessionFactory before, if still open.
     * Note that this method should only be used with static SessionFactory
     * management, not with JNDI or any other external registry.
    */
    public static void rebuildSessionFactory() {
        log.debug("Using current Configuration for rebuild.");
        rebuildSessionFactory(configuration);
    }

   /**
    * Rebuild the SessionFactory with the given Hibernate Configuration.
    * <p>
     * HibernateUtil does not configure() the given Configuration object,
     * it directly calls buildSessionFactory(). This method also closes
     * the old SessionFactory before, if still open.
     *
    * @param cfg
    */
    public static void rebuildSessionFactory(Configuration cfg) {
        log.debug("Rebuilding the SessionFactory from given Configuration.");
      synchronized(sessionFactory) {
            if (sessionFactory != null && !sessionFactory.isClosed())
                sessionFactory.close();
            if (useJNDIBinding)
                cfg.buildSessionFactory();
            else
                sessionFactory = cfg.buildSessionFactory();
            configuration = cfg;
      }
    }

   /**
    * Retrieves the current Session local to the thread.
    * <p/>
    * If no Session is open, opens a new Session for the running thread.
    *
    * @return Session
    */
   public static Session getSession() {
        if (useThreadLocal) {
            Session s = (Session) threadSession.get();
            if (s == null) {
                log.debug("Opening new Session for this thread.");
                s = getSessionFactory().openSession();
                threadSession.set(s);
            }
            return s;
        } else {
            return getSessionFactory().getCurrentSession();
        }
   }

   /**
    * Closes the Session local to the thread.
    */
   public static void closeSession() {
        if (useThreadLocal) {
            Session s = (Session) threadSession.get();
            threadSession.set(null);
            if (s != null && s.isOpen()) {
                log.debug("Closing Session of this thread.");
                s.close();
            }
        } else {
            log.warn("Using CMT/JTA, intercepted superfluous close call.");
        }
   }

   /**
    * Start a new database transaction.
    */
   public static void beginTransaction() {
        if (useThreadLocal) {
            Transaction tx = (Transaction) threadTransaction.get();
            if (tx == null) {
                log.debug("Starting new database transaction in this thread.");
                tx = getSession().beginTransaction();
                threadTransaction.set(tx);
            }
        } else {
            log.warn("Using CMT/JTA, intercepted superfluous tx begin call.");
        }
   }

   /**
    * Commit the database transaction.
    */
   public static void commitTransaction() {
        if (useThreadLocal) {
            Transaction tx = (Transaction) threadTransaction.get();
            try {
                if ( tx != null && !tx.wasCommitted()
                                && !tx.wasRolledBack() ) {
                    log.debug("Committing database transaction of this thread.");
                    tx.commit();
                }
                threadTransaction.set(null);
            } catch (RuntimeException ex) {
                log.error(ex);
                rollbackTransaction();
                throw ex;
            }
        } else {
            log.warn("Using CMT/JTA, intercepted superfluous tx commit call.");
        }
   }

   /**
    * Rollback the database transaction.
    */
   public static void rollbackTransaction() {
        if (useThreadLocal) {
            Transaction tx = (Transaction) threadTransaction.get();
            try {
                threadTransaction.set(null);
                if ( tx != null && !tx.wasCommitted() && !tx.wasRolledBack() ) {
                    log.debug("Tyring to rollback database transaction of this thread.");
                    tx.rollback();
                    log.debug("Database transaction rolled back.");
                }
            } catch (RuntimeException ex) {
                throw new RuntimeException("Might swallow original cause, check ERROR log!", ex);
            } finally {
                closeSession();
            }
        } else {
            log.warn("Using CMT/JTA, intercepted superfluous tx rollback call.");
        }
   }

   /**
    * Reconnects a Hibernate Session to the current Thread.
    *
    * @param session The Hibernate Session to be reconnected.
    */
   public static void reconnect(Session session) {
        if (useThreadLocal) {
            log.debug("Reconnecting Session to this thread.");
            session.reconnect();
            threadSession.set(session);
        } else {
            log.error("Using CMT/JTA, intercepted not supported reconnect call.");
        }
   }

   /**
    * Disconnect and return Session from current Thread.
    *
    * @return Session the disconnected Session
    */
   public static Session disconnectSession() {
        if (useThreadLocal) {
            Session session = getSession();
            threadSession.set(null);
            if (session.isConnected() && session.isOpen()) {
                log.debug("Disconnecting Session from this thread.");
                session.disconnect();
            }
            return session;
        } else {
            log.error("Using CMT/JTA, intercepted not supported disconnect call.");
            return null;
        }
   }

   /**
    * Register a Hibernate interceptor with the current SessionFactory.
    * <p>
    * Every Session opened is opened with this interceptor after
    * registration. Has no effect if the current Session of the
    * thread is already open, effective on next close()/getSession().
     * <p>
     * Attention: This method effectively restarts Hibernate. If you
     * need an interceptor active on static startup of HibernateUtil, set
     * the <tt>hibernateutil.interceptor</tt> system property to its
     * fully qualified class name.
    */
   public static void registerInterceptorAndRebuild(Interceptor interceptor) {
        log.debug("Setting new global Hibernate interceptor and restarting.");
      configuration.setInterceptor(interceptor);
        rebuildSessionFactory();
   }

   public static Interceptor getInterceptor() {
        return configuration.getInterceptor();
   }

}


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 02, 2005 12:16 pm 
Beginner
Beginner

Joined: Thu Aug 04, 2005 8:41 pm
Posts: 47
Thanks. This helps a lot.

However what about applications which use hibernate to talk to several
database servers? HibernateUtil is a static class.

HibernateUtil code grew in size and one wouldn't like to clone it for
each database server.

GenericHibernateDAO is using HibernateUtil in its constructor.
I think it should give a user at least ability to override the way it
gets Session.

So instead of
Code:
    Session session;

    public GenericHibernateDAO(Class persistentClass) {
        this.persistentClass = persistentClass;

        // Get a Session and begin a database transaction. If the current
        // thread already has an open Session and an ongoing Transaction,
        // this is a no-op and only assigns the Session to the member variable.
        session = HibernateUtil.getSession();
        HibernateUtil.beginTransaction();
    }


I would rather had something like (without session member variable)

Code:
    protected Session currentSession() {
        return HibernateUtil.currentSession();
    }

    public GenericHibernateDAO(Class persistentClass) {
        this.persistentClass = persistentClass;

        HibernateUtil.beginTransaction();
    }


and everywhere in the code instead of referencing to session member variable
use currentSession() method. At least it would let users of GenericHibernateDAO to override currentSession() and do not depend
completely on HibernateUtil.getSession() in cases when HibernateUtil
is not enough for them.

Thanks,
--MG


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 23 posts ]  Go to page 1, 2  Next

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.