Continues from Part I
Configuring Hibernate Framework
The instance oforg.hibernate.cfg.Configuration
is used to manage Hibernate configuration that represents an
entire set of mappings of an application’s Java types to an SQL
database. These mappings are compiled from various XML mapping files
or from Java 5 Annotations. The org.hibernate.cfg.Configuration is
used to build an immutable org.hibernate.SessionFactory
.
Hibernate provides following types of configurations
- hibernate.cfg.xml – A standard XML file which contains hibernate configuration and which resides in root of application’s CLASSPATH
- hibernate.properties – A Java compliant property file which holds key value pair for different hibernate configuration strings.
Programmatic
configuration – This is the manual approach. The configuration
can be defined in Java classes.
It
is noticeable here that if both hibernate.cfg.xml and
hibernate.properties files are provided simultaneously in an
application then hibernate.cfg.xml gets precedence over
hibernate.properties.
Pros and Cons of Hibernate Framework
Pros
- Reduces development effort for persisting data from business tier.
- With help of object relational mapping the database operations on tables can be performed in an object oriented style.
- It allows caching of persistent objects that can be used for the performance improvement.
- It is database vendor neutral.
- Developer is not required to know SQL statements.
Cons
- For highly data intensive applications, hibernate does not perform up to the mark due to memory issues.
- Hibernate is suitable only if the database schema is well designed and not very complex.
- Testing and debugging memory leaks might take a lot of time because the framework does not provide the control to write an optimized query.
- Sometimes hibernate may generate a vendor specific query in the background that is performance wise very poor.
- Needs high level of expertise for handling the performance tuning issues.
Using SessionFactory, Session and Transaction objects in an application
If there is single data source to which application needs to connect then only one SessionFactory object is enough for the entire application. However if an application is supposed to be connected to multiple data sources then in that case the number of SessionFactory objects in the application should be equal to the number of data sources. In most of the cases it is
the best practice to use single hibernate session for a single
request. If we tend to use same session for multiple requests then it
means more number of persistent objects will reside in the first
level cache which is a memory leak and thus not desired.
Whenever a Servlet receives
any request a new thread is assigned to that request and if we
follow the session-per-request approach then we will always have a
unique session for each thread created against each request.
Now the question is how we
can ensure that for each thread there is always a unique session? Simply use
ThreadLocal variables to handle Hibernate sessions and that is it. It is noticeable that although a session can span over multiple transactions yet on a given point of time there can not be more than one active transactions as in Hibernate there is no concept of nested transactions. Therefore we can also use ThreadLocal variables to handle Hibernate transactions. Please
take a look at the following utility class that does provide this
functionality.
import java.sql.SQLException; import java.util.logging.Logger; import org.hibernate.Transaction; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; public final class HibernateUtil { private static final Logger logger = Logger.getLogger(HibernateUtil.class.getSimpleName()); private static final ThreadLocal<Session> threadSession = new ThreadLocal<Session>(); private static final ThreadLocal<Transaction> threadTransaction = new ThreadLocal<Transaction>(); private final static SessionFactory sessionFactory; static { sessionFactory = buildSessionFactory(); } private static SessionFactory buildSessionFactory() { try { // Create the SessionFactory from hibernate.cfg.xml return new AnnotationConfiguration() .configure() .buildSessionFactory(); } catch (Throwable ex) { logger.warning("Initial SessionFactory creation failed."); throw new ExceptionInInitializerError(ex); } } public static Session getCurrentSession() { Session s = threadSession.get(); try { if (s == null || !s.isOpen()) { logger.info("Opening new Session for this thread."); s = sessionFactory.openSession(); threadSession.set(s); } else { logger.info("Using current session in this thread."); } } catch (HibernateException ex) { throw new IllegalStateException("unable to open hibernate session",ex); } return s; } public static WrappedSession getWrappedSession() { return new WrappedSession(getCurrentSession()); } public static void closeSession() { try { final Session s = threadSession.get(); if (s != null && s.isOpen()) { logger.info("Closing Session of this thread."); s.close(); } } catch (HibernateException ex) { logger.warning("Failed to close session of this thread."); } finally { threadSession.set(null); } } public static void beginTransaction() { Transaction tx = threadTransaction.get(); try { if (tx != null && !tx.isActive()) { tx = null; threadTransaction.set(null); } if (tx == null) { logger.info("Starting new database transaction in this thread."); tx = getCurrentSession().beginTransaction(); threadTransaction.set(tx); } else { logger.info("Using current database transaction in this thread."); } } catch (HibernateException ex) { throw new IllegalStateException("Failed to start new database transaction in this thread."); } finally { if (threadSession.get() == null || !threadSession.get().isOpen()) { getCurrentSession(); } } } public static void commitTransaction() { final Transaction tx = threadTransaction.get(); try { if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) { logger.info("Flushing session and committing transaction of this thread."); Session s = getCurrentSession(); s.flush(); tx.commit(); } } catch (HibernateException ex) { rollbackTransaction(); logger.warning("Failed to flush session and commit transaction of this thread."); } finally { threadTransaction.set(null); closeSession(); } } public static void rollbackTransaction() { final Transaction tx = threadTransaction.get(); try { if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) { logger.info("Trying to rollback database transaction of this thread."); tx.rollback(); } } catch (HibernateException ex) { logger.warning("Failed to rollback database transaction of this thread."); } finally { threadTransaction.set(null); closeSession(); } } }