HibernateUtil - Managing the Hibernate
Session
One of the most resource intensive components you will bump into in the Hibernate API is the SessionFactory. The SessionFactory is a very important resource, built upon the AnnotationConfiguration object, and responsible for the generation of Hibernate Session objects that are required any time Hibernate is to perform some type of database interaction.
Of course, the Hibernate SessionFactory is not a component you want to be creating over and over again, as we have been doing in all of the examples so far in these tutorials. Instead, you want to strictly manage how SessionFactory instances are instantiated and accessed within your application. While there are many approaches to doing this, perhaps the most common is the implementation of a HibernateUtil class that manages the SessionFactory, while providing other pieces of common functionality that many parts of a Hibernate application might need.
The Hibernate helper class called HibernateUtil will be the focus of this tutorial, as we create a class that helps by:
- localizing the addition of JPA annotated classes to the AnnotationConfiguration object
- mitigating access to the Hibernate SessionFactory and
Session objects
- providing methods for performing the most common
transaction based functions
- having a runnable main method that can be used to recreate
the underlying database
Creating the HibernateUtil Class
Over and over again in these tutorials, we have coded all of the Hibernate plumbing required to perform the most basic of operations. That's not a good practice. Instead, we want to localize the initialization of our Hibernate Configuration, while at the same time, mitigate access to important data layer resources. To do just that, we're going to create a fancy little class named HibernateUtil.
package com.examscam;
public class HibernateUtil { }Now, the most dangerous part of using JPA annotations is doing all sorts of Hibernate code, but forgetting to add all of the annotated classes you are using to you AnnotationConfiguration object. We've only been using the User class, so we've only got one class to add, but you could imagine that as you add classes, it would become dangerously easy to forget to mention an important class when you're doing testing, especially if you're creating the Hibernate SessionFactory from scratch every time. So, one of the first things I like to add to my HibernateUtil class is a simple method that initializes and configures the AnnotationConfiguration object. This becomes the single, central place where all of the JPA annotated classes are added to the configuration, and since the method will actually return the AnnotationConfiguration object, it can be used either to create a new SessionFactory, or do other kewl Hibernate stuff like being passed to the SchemaExport object to recreate the underlying database. Here's how the getInitailizedConfiguration() method looks:
public static Configuration getInitializedConfiguration() {
AnnotationConfiguration config = new AnnotationConfiguration();
/* add all of your JPA annotated classes here!!!*/
config.addAnnotatedClass(User.class);
config.configure();
return config;
}
The Static
getInitializedConfiguration() Method
The getInitializedConfiguration() method simply declares, initializes, configures and subsequently returns the ever so useful and important Hibernate Configuration object, of which AnnotationConfiguration is a subclass.
This method becomes the one, single, solitary place in the entire application where all of the JPA annotated Java classes are added to the Hibernate configuration. Remember, even if you decorate your JavaBeans with JPA annotations, if those beans are not added to the Hibernate Configuration object before the SessionFactory is instantiated, well, the Hibernate framework will not be able to manage the persistent state of those JavaBeans. Having JPA annotated classes added to the configuration in one, single, solitary place, is important for managing, maintaining, and iterating over the development of your applications.
public static Configuration getInitializedConfiguration(){
AnnotationConfiguration config
= new AnnotationConfiguration();
/* add all of your JPA annotated classes here!!!*/
config.addAnnotatedClass(User.class);
/*Future classes we will be creating.
Keep them commented out for now.*/
//config.addAnnotatedClass(Snafu.class);
//config.addAnnotatedClass(FooBar.class);
//config.addAnnotatedClass(Thing.class);
//config.addAnnotatedClass(Team.class);
//config.addAnnotatedClass(Player.class);
//config.addAnnotatedClass(RightManyCourse.class);
//config.addAnnotatedClass(LeftManyStudent.class);
config.configure();
return config;
}The recreateDatabase() Method
The return type from the getInitializedConfiguration method will be used in two very interesting ways in our HibernateUtil class, with the first devious little usage being to recreate the database. As we have seen before, the SchemaExport class, when passed an initialized Configuration object, can generate a database create script based upon the various JPA annotated classes that have been added to the AnnotationConfiguration instance. From there, it can take that create script and execute it against the database of interest, dropping all the defined tables before recreating them.
The recreateDatabase() method is a very helpful, yet dangerous method, that I like to define in the HibernateUtil class.
import org.hibernate.tool.hbm2ddl.SchemaExport;
public static void recreateDatabase() {
Configuration config;
config =
HibernateUtil.getInitializedConfiguration();
new SchemaExport(config).create(true, true);
}The recreateDatabase method can come in especially handy during various application testing routines, where the database is recreated, queries are executed against the database using staged data, and then deleted once the test case has been completed.
If we add a runnable main method to the HibernateUtil class, and call the recreateDatabase() method, we have a nice, single class we can run whenever we want to reinitialize our persistence store:
public static void main(String args[]) {
HibernateUtil.recreateDatabase();
}Getting the Hibernate Session
With the configuration object initialized, and the recreateDatabase method out of the way, it's time to get deep into the getSession() method of the HibernateUtil class.
If you've done any work with Hibernate, you know that the key to performing database operations is the magical Hibernate Session object. The Hibernate Session is a fairly lightweight component, although it is created by a fairly heavyweight, resource intensive component called a SessionFactory. Correspondingly, any good Hibernate application should efficiently manage a SessionFactory object, creating it within a method employing a singleton type of design pattern, and subsequently caching the SessionFactory as a static variable. From this static, cached instance of the SessionFactory, we can pump out Hibernate Session objects ad nauseum.
For our HibernateUtil class, we'll implement this singleton design pattern inside the static getSession() method, while at the same time, declaring the SessionFactory as a private, static variable for the class. Here's how it looks:
public class HibernateUtil {
private static SessionFactory factory;
public static Session getSession() {
if (factory == null) {
Configuration config =
HibernateUtil.getInitializedConfiguration();
factory = config.buildSessionFactory();
}
Session hibernateSession = factory.getCurrentSession();
return hibernateSession;
}
}
package com.examscam;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import com.examscam.model.User;
/* The HibernateUtil class so far. */
public class HibernateUtil {
private static SessionFactory factory;
public static Configuration getInitializedConfiguration() {
AnnotationConfiguration config =
new AnnotationConfiguration();
/* add all of your JPA annotated classes here!!!*/
config.addAnnotatedClass(User.class);
config.configure();
return config;
}
public static Session getSession() {
if (factory == null) {
Configuration config =
HibernateUtil.getInitializedConfiguration();
factory = config.buildSessionFactory();
}
Session hibernateSession =
factory.getCurrentSession();
return hibernateSession;
}
public static void recreateDatabase() {
Configuration config;
config=HibernateUtil.getInitializedConfiguration();
new SchemaExport(config).create(true, true);
} public static void main(String args[]) {
HibernateUtil.recreateDatabase();
}
}
beginTransaction()
With the exception of a few query calls, database manipulations must be done within the scope of a transaction. As a result, client applications must have the ability to both begin a transaction, and subsequently, ask the Hibernate framework to commit any changes that have occurred during the scope of that transaction. To facilitate this in our application, we will allow client applications to begin transactions by creating a publicly accessible beginTransaction() method, along with a corresponding public commitTransaction() method, in the HibernateUtil class.
public static Session beginTransaction(){
Session hibernateSession;
hibernateSession= HibernateUtil.getSession();
hibernateSession.beginTransaction();
return hibernateSession;
}Notice how the beginTransaction() method actually returns the Session object. This is simply a helpful little twist that allows a client to both begin a transaction and gain access to the Hibernate session, all at the same time.
The commitTransaction() Method
Now, in corollary with the beginTransaction() method, the HibernateUtil class needs a commitTransaction() method as well. The commitTransaction() method is relatively straight forward, encapsulating the commit() method call on the Hibernate Session's transaction.
public static void commitTransaction(){
HibernateUtil.getSession()
.getTransaction().commit();
}Closing the Hibernate Session
Seeing that we have a method to get the Hibernate Session object, it's only polite that we also provide a method to close the Hibernate session.
This method is pretty simple, as it simply uses the getSession() method of the HibernateUtil class to get the current working session, and then subsequently calls the Session's close method. Having a helper method like this tends to make your final application code look a little bit tidier.
public class HibernateUtil {
private static SessionFactory factory;
public static void closeSession() {
HibernateUtil.getSession().close();
}
}
The rollbackTransaction() Method
Of course, if a transaction is going to begin, and eventually be committed, we have to keep in mind the fact that something might go wrong, and the transaction might need to be rolled back. For this purpose, a good HibernateUtil class should have a helpful little rollbackTransaction method as well.
public static void rollbackTransaction(){
HibernateUtil.getSession()
.getTransaction().rollback();
}The Whole Darned HibernateUtil Class
package com.examscam;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import com.examscam.model.User;
public class HibernateUtil {
private static SessionFactory factory;
public static Configuration
getInitializedConfiguration() {
AnnotationConfiguration config =
new AnnotationConfiguration();
/* add all of your JPA annotated classes here!!! */
config.addAnnotatedClass(User.class);
config.configure();
return config;
}
public static Session getSession() {
if (factory == null) {
Configuration config =
HibernateUtil.getInitializedConfiguration();
factory = config.buildSessionFactory();
}
Session hibernateSession =
factory.getCurrentSession();
return hibernateSession;
}
public static void closeSession() {
HibernateUtil.getSession().close();
}
public static void recreateDatabase() {
Configuration config;
config =
HibernateUtil.getInitializedConfiguration();
new SchemaExport(config).create(true, true);
}
public static Session beginTransaction() {
Session hibernateSession;
hibernateSession = HibernateUtil.getSession();
hibernateSession.beginTransaction();
return hibernateSession;
}
public static void commitTransaction() {
HibernateUtil.getSession()
.getTransaction().commit();
}
public static void rollbackTransaction() {
HibernateUtil.getSession()
.getTransaction().rollback();
}
public static void main(String args[]) {
HibernateUtil.recreateDatabase();
}
}Code Without the HibernateUtil Class
So, we've worked our fingers to the bone creating a HibernateUtil class, employing a singleton type of implementation to minimize the creation of SessionFactory objects, while at the same time, making it relatively easy to obtain the magical and highly helpful Hibernate Session object. So, how would the use of the HibernateUtil class look in code? Well, lets take a look at a code sample from a previous chapter:
package com.examscam.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
@Entity
public class User {
private int id;
private String password;
@Id
@GeneratedValue
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public String getPassword() {return password;}
public void setPassword(String password) {
this.password = password;
}
public static void main(String args[]){
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.configure();
SessionFactory factory = config.buildSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
System.out.println("creating user");
User u = new User();
u.setPassword("abc123");
session.saveOrUpdate(u);
System.out.println("user saved");
session.getTransaction().commit();
System.out.println("transaction successful!!!");
}
}An Improved main using HibernateUtil
As you can see with the code below, when the HibernateUtil class is used, the main method looks much cleaner, with no need to initialize or create an AnnotationConfiguration object. Simplified calls to the HibernateUtil class provides easy access to the Hibernate Session, and it is easy to access important Session functionality such as the beginning and committing of database transactions.
package com.examscam.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
@Entity
public class User {
private int id;
private String password;
@Id
@GeneratedValue
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public String getPassword() {return password;}
public void setPassword(String password) {
this.password = password;
}
public static void main(String args[]){
Session session =
HibernateUtil.beginTransaction();
System.out.println("creating user");
User u = new User();
u.setPassword("abc123");
session.saveOrUpdate(u);
System.out.println("user saved");
HibernateUtil.commitTransaction();
System.out.println("transaction successful!!!");
}
}
|