Creating DAOs with Hibernate 3.5 & JPA 2.0 Annotations
This tutorial will help you learn how to create good DAO components that can be easily integrated into a Spring based application. This tutorial concentrates on the Data Access Objects. The next lesson will deal with Spring 3 integration.
Creating Good DAOs with Hibernate 3.5 and JPA 2.0 Annotations
In previous Hibernate and JPA tutorials, we configured a 3.5 environment for development and testing, and went through the steps of persisting a very simple POJO, named the GameSummary, to the database. We even created a little class, named the HibernateCrudRunner, that went through the process of creating a persistent object, updating that object, querying that object, and finally, deleting that object. Yeah, it was a pretty awesome tutorial.
But the problem with the application code was that it made direct calls to the Hibernate Session, and the transaction management was inconsistent and messy. In production applications, you want to minimize the exposure your client applications have to the internal workings of your persistence mechanism, and the best way to do that is to create fairly generic Data Access Object. And that's exactly what I'm going to do here using Hibernate and JDBC. You can also run this on an ODBC compliant database if you use an ODBC and JDBC driver.
By the way, you can find a really awesome and advanced tutorial on creating DAOs right here: Coding Advanced DAOs with Hibernate and JPA Annotations - The Pattern Our DAOs are going to be pretty awesome in their own right, but that tutorial goes really in depth about DAOs and the Factory Pattern and such. Once you've mastered this tutorial, you'll be ready to really advance your knowledge of DAOs by heading over there. But first things first, and with that saying in mind, the first thing we need is a generic interface for our DAOs:
The Very Generic GameSummaryDAO Interface
Our GameSummaryDAO will simply define four methods: save, delete, findByPrimaryKey and findAll.
package com.mcnz.dao;
import java.io.*;
import java.util.*;
public interface GameSummaryDAO < T, ID extends Serializable >{
T save( T entity);
void delete( T entity);
T findByPrimaryKey( ID id);
List < T > findAll();
}
We've thrown around a few generics in there, namely the T and the ID. Basically, this will simply ensure that every DAO is associated with a serializable primary key (ID), and a specific Java class (T). For this application, our ID will be of type Long, and of course, our associated Java class is the GameSummary class.
You can see how in the class declaration of the HiberanteGameSummaryDAO how the ID and T attributes are provided concrete values:
package com.mcnz.dao;
import java.util.*;
import org.hibernate.*;
import com.mcnz.model.*;
public class HibernateGameSummaryDAO implements GameSummaryDAO <GameSummary, Long> {
}
Implementation Code In, Transaction Details Out
Now, the rule for creating DAOs is that you keep implementation details inside of them so that the implementation isn't exposed to the client. And even more importantly, you keep transaction details outside of them. I can't tell you how many times I've seen people starting and stopping transactions inside of a DAO method. That is wrong, wrong, wrong, wrong wrong! "Implementation code in, transaction details out," that should be your mantra when coding DAOs. Since we're using Hibernate, the key Hibernate component each DAO needs is a SessionFactory. So, you'll need to declare a SessionFactory instance, define a setter method for that SessionFactory, and also declare a constructor that takes a SessionFactory as an argument. We'll also add in a default constructor, just for fun, but be warned, if someone calls this default constructor and doesn't set a fully mature SessionFactory into that DAO after it is created, you'll be in a world of NullPointerException hurt.
public class HibernateGameSummaryDAO implements GameSummaryDAO <GameSummary, Long>{
private SessionFactory sessionFactory = null;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public HibernateGameSummaryDAO() {}
public HibernateGameSummaryDAO (SessionFactory sessionFactory) {
setSessionFactory(sessionFactory);
}
}
Implementing & Overriding the Inherited Methods
With the pleasantries of coding a constructor and declaring our SessionFactory instance, we can go ahead and implement the findAll, findByPrimaryKey, save and delete methods. There's really nothing too interesting about them; they simply invoke the corresponding method on the Hibernate Session, each of which we spoke about in the previous tutorial. Here's the completed class, import statements and all:
package com.mcnz.dao;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.mcnz.model.GameSummary;
public class HibernateGameSummaryDAO implements GameSummaryDAO <GameSummary, Long>{
private SessionFactory sessionFactory = null;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public HibernateGameSummaryDAO(){}
public HibernateGameSummaryDAO(SessionFactory sessionFactory){
setSessionFactory(sessionFactory);
}
@Override
public List findAll() {
Session session = sessionFactory.getCurrentSession();
Query queryResult = session.createQuery("from GameSummary");
return queryResult.list();
}
@Override
public GameSummary findByPrimaryKey(Long id) {
Session session = sessionFactory.getCurrentSession();
Object o = session.load(GameSummary.class, id);
return (GameSummary)o;
}
@Override
public GameSummary save(GameSummary entity) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(entity);
return entity;
}
@Override
public void delete(GameSummary entity) {
sessionFactory.getCurrentSession().delete(entity);
}
}
There are a few interesting changes I have made when compared to a previous tutorial. First of all, I used saveOrUpdate in the save method, rather than having separate create and update methods. With the saveOrUpdate method, Hibernate will create a new representation for any entity it is passed that doesn't have a propery primary key, aka id field. If the entity passed in does have an id field, Hibernate will simply perform an update.
Also, I used the load method in the findByPrimaryKey method, as opposed to the get method. The load method is a bit easier on the database, as it doesn't load in every piece of data about the entity, but it does open the door for LazyLoadingExceptions down the road. I typically use get instead of load, but I thought I'd just demonstrate the load method here for andragogal purposes. Here's a more in depth look at the differences between the two methods: The Difference Between the Hibernate Session's Load and Get Methods
Using a DAO in Client Applications
So, we have our DAO. Now all we need is a client application to call it.
I'm going to create a simple little class called the CrudRunnerDAO that will have a simple main method. Since our DAO needs a SessionFactory, the first thing we'll need to do is create a SessionFactory. Once the SessionFactory is created, we create an instance of our JDBC DAO by feeding it to the HibernateGameSummaryDAO's constructor.
package com.mcnz.dao;
import org.hibernate.*;
import org.hibernate.cfg.*;
import com.mcnz.model.*;
public class CrudRunnerDAO {
public static void main(String[] args) {
/* Initialize the Hibernate Environment */
AnnotationConfiguration config = new AnnotationConfiguration();
config.addAnnotatedClass(GameSummary.class);
config.configure();
SessionFactory sessionFactory = config.buildSessionFactory();
/* Create the DAO, passing in the SessionFactory */
GameSummaryDAO dao = new HibernateGameSummaryDAO(sessionFactory);
/* more to come!!! */
}
}
Adding Records with the DAO
With the DAO created and properly initialized, we can go ahead and create instances and save them to the database. Notice that while the DAO takes care of the actual saving and persisting of the data, the client application is responsible for demarcating the transaction.
/* Create a record and keep track of its assigned id */
Long id = null;
{
GameSummary gs = new GameSummary();
gs.setResult("tie");
System.out.println(gs);
sessionFactory.getCurrentSession().beginTransaction();
dao.save(gs);
id = gs.getId();
sessionFactory.getCurrentSession().getTransaction().commit();
System.out.println(gs);
}
Loading and Updating a Record with the Hibernate DAO
The previous code snippet held onto the unique primary key the database assigned our entity when it was saved to the datastore. Using this id, we will now fetch the entity that was just saved. Note that we can do this, as the previous transaction has finished, and because of the placement of the curly braces, the GameSummary instance that was created earlier is now out of scope.
/* Find and update the created record that has gone out of scope */
sessionFactory.getCurrentSession().beginTransaction();
GameSummary gs = (GameSummary) dao.findByPrimaryKey(id);
gs.setResult("win");
sessionFactory.getCurrentSession().getTransaction().commit();
Querying the Database
After updating the record, we do a fully query on the database using the findAll method of the DAO. We print out each record returned from this query, and notice that the record with our current id has had the result property updated from a tie to a win.
Deleting a Record from the Database Using the Hibernate DAO
Finally, we delete the record we created from the database. When this JDBC transaction is complete, we create another transaction, and print out a findAll query from the database. As this Hibernate query runs, we will see that the record that we created at the beginning of this method has been permanently deleted.