Hibernate 3.5 Without Hibernate: Migrating to the Java Persistence API
Here we look at using the Hibernate framework as our persistence mechanism, but only referencing the JPA libraries in our code. This way, your persistence code is not bound to the vendor that implements your persistence architecture.
Using Hibernate 3.5 as Your JPA 2.0 Implementation Provider
How to Learn Hibernate
Hibernate Made Easy WebSite
Video Tutorials on Learning Hibernate
Recommended Books for Learning Hibernate
Check out this updated Tutorial for Spring 3 as well.
In celebration of the summer season, we've been running a few tutorials on the difference between Hibernate and JDBC.
In our first tutorial, we showed you everything you needed to do to configure a basic Java development environment that leverages Hibernate 3.5 and all of its associated and required libraries.
In our second tutorial, we decorated a very simple JavaBean with a few JPA 2.0 annotations, and then demonstrated how we could write some Hibernate code that consumes that JavaBean and performs some persistence magic for us.
Of course, no good deed goes unpunished at TheServerSide.com, so the initial feedback, like the response we got from Mr. Christian Gossart, was that it was a great tutorial, but it wasn't really a Hibernate vs JPA tutorial because it was using the Hibernate Session and other classes in the Hibernate API.
"Well, ... apart from the annotations on the GameSummary entity class ... this tutorial is Hibernate specific, and cannot be used with another JPA provider (be it EclipseLink or OpenJPA)." -Mr. Christian Gossart
Well, we aim to please here at TheServerSide.com, and we thought it would be neat to actually show you both sides of the coin; that is, performing data persistence with the Hibernate API, as we did in the previous tutorial, and now, showing how the same thing could be done using only the Java Persistence API, and no reference to Hibernate.
It should be noted that Hibernate is still the persistence provider. So, if you want to write and test this application, you need to follow all the same steps that were performed in the first tutorial that dealt with setting up a Hibernate development environment. The persistence implementation is all Hibernate under the covers. The only difference is that we're using the Java Persistence API to communicate with it.
Revisiting the JPA Annotated POJO
Here is what our JPA annotated GameSummary bean looks like. This is the exact code that was used in the two tutorials that were mentioned earlier. Notice that there is nothing Hibernate specific in this code; it's just a plain old Java object (POJO):
package com.mcnz.model;
import javax.persistence.*;
@Entity
public class GameSummary {
@Id
@GeneratedValue
private Long id;
private String result;
public Long getId() {return id;}
public void setId(Long id) {
this.id = id;
}
public String getResult() {return result;}
public void setResult(String result) {
this.result = result;
}
public String toString() {
return "id: " + id + " result: " + result ;
}
}
Now the offending code that contained all of the Hibernate API references was the little main method that I threw into that GameSummary class which performed some database functions. Here's what the runnable main method, which saved a GameSummary instance to the database, looked like when we leveraged the Hibernate API:
public static void main(String args[]) {
AnnotationConfiguration config = new AnnotationConfiguration();
config.addAnnotatedClass(GameSummary.class);
config.configure();
//new SchemaExport(config).create(true, true);
GameSummary gs = new GameSummary();
gs.setResult("win");
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(gs);
session.getTransaction().commit();
}
Factoring Out the Hibernate API Calls
So, how could we do all of that without referencing the Hibernate API? Well, it's pretty simple, actually. It's almost an even swap between the JPA components and Hibernate components. Take a look at the following JpaRunner class, which does exactly the same thing as the main method above, with the exception of the fact that it doesn't reference the Hiberante API (note that this will not run successfully until you have configured a persistence.xml file, which we will do next):
package com.mcnz.model;
import javax.persistence.*;
public class JpaRunner {
public static void main(String args[]){
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PU");
EntityManager em = emf.createEntityManager();
GameSummary gs = new GameSummary();
gs.setResult("win");
em.getTransaction().begin();
em.persist(gs);
em.getTransaction().commit();
}
}
Comparing Hibernate vs Java Persistence API Components
As you can see, the JPA code is pretty similar to the Hibernate code, except instead of a Hibernate SessionFactory, you have the EntityManagerFactory, and instead of a Hibernate Session, you have an EntityManager. Other than that, the biggest difference between those two code snippets is the size of the font.
Now, there is one major change that needs to be made to the environment before the JPA code we just wrote can be tested: we need a persistence.xml file, which must be placed in a META-INF folder on the runtime classpath.
Here's my persistence.xml file:
The persistence.xml File
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="PU">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.mcnz.model.GameSummary</class>
<exclude-unlisted-classes />
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost/rps"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="password"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="false"/>
</properties>
</persistence-unit>
</persistence>
persistence.xml vs hibernate.cfg.xml
Again, the persistence.xml file really isn't that different from the hiberante.cfg.xml file, which is what you would expect, especially considering that it is the Hibernate and JDBC framework that is actually implementing the JPA specification for us behind the scenes. One thing that is important to note is the name given to the persistence-unit: PU.
<persistence-unit name="PU">
This name, PU, is the name used when you initialize your JPA EntityManagerFactory in your code:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PU");
Oh, and just for comparison purposes, here is what the old hibernate.cfg.xml file from the previous tutorial looks like:
The Old hibernate.cfg.xml File
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">
jdbc:mysql://localhost/rps
</property>
<property name="connection.username">
root
</property>
<property name="connection.password">
password
</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="current_session_context_class">
thread
</property>
<!-- this will show us all sql statements -->
<property name="hibernate.show_sql">
true
</property>
<!-- mapping files NOTICE WE HAVE NONE-->
</session-factory>
</hibernate-configuration>
Running the JpaRunner
When the JpaRunner is executed, a new record is added to the database, and the application logs show the following entry:
Hibernate: insert into GameSummary (clientGesture, date, result, serverGesture) values (?, ?, ?, ?)
Again, the logs reference Hiberante, because Hibernate JDBC is providing the implementation, but our code is completely shielded from any of those Hibernate related implementation details.
A Full Migration of the HibernateCrudRunner
The "grand finale" of the previous tutorial was a class called the HibernateCrudRunner, which created all sorts of transactions, and performed queries, updates, creates and delete operations on a very unsuspecting GameSummary instance, all using Hibernate. It's a big class, so I'm loath to reproduce it again here. Just scroll down to the bottom of this link and you'll see it. So, to really see how JPA works, I figured I'd reproduce that class in its entirety, but with nothing but JPA calls, and no reference to Hibernate. Here's how the JPA implementation of the HibernateCrudRunner looks:
package com.mcnz.model;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
public class JpaRunner {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PU");
EntityManager entityManager = emf.createEntityManager();
Long entityId = null;
{
GameSummary gameSummary = new GameSummary();
gameSummary.setResult("win");
System.out.println(gameSummary);
JpaRunner.save(entityManager, gameSummary);
entityId = gameSummary.getId();
System.out.println(gameSummary);
}
GameSummary gameSummary
= JpaRunner.findByPrimaryKey(entityManager, entityId);
gameSummary.setResult("tie");
JpaRunner.update(entityManager, gameSummary);
JpaRunner.findAll(entityManager);
JpaRunner.delete(entityManager, gameSummary);
JpaRunner.findAll(entityManager);
}
public static void save(EntityManager em, GameSummary gs) {
em.getTransaction().begin();
em.persist(gs);
em.getTransaction().commit();
}
public static void update(EntityManager em, GameSummary gs) {
em.getTransaction().begin();
em.persist(gs);
em.getTransaction().commit();
}
public static java.util.List findAll(EntityManager em) {
em.getTransaction().begin();
Query queryResult = em.createQuery("from GameSummary");
java.util.List allSummaries = queryResult.getResultList();
for (int i=0;i<allSummaries.size();i++) {
GameSummary gs = (GameSummary) allSummaries.get(i);
System.out.println(gs.toString());
}
em.getTransaction().commit();
return allSummaries;
}
public static GameSummary findByPrimaryKey(EntityManager em, Long id) {
em.getTransaction().begin();
GameSummary gs = (GameSummary) em.find(GameSummary.class, id);
em.getTransaction().commit();
return gs;
}
public static void delete(EntityManager em, GameSummary gs) {
em.getTransaction().begin();
em.remove(gs);
em.getTransaction().commit();
}
}
You can see why I was loath to reproduce the original Java code for the class, as it is rather long. However, if you do compare the JPA implementation to the one that uses Hibernate, you'll see that the two are fairly similar. Sure, instead of a delete call, we have a remove call; and instead of a call to update or save, we have a call to persist, but otherwise, the two implementations are fairly similar.
And that's it! Okay, maybe that's not it, but it is a start. As you can see, if you have some experience with Hibernate, working with the Java Persistence API is fairly straight forward, and if you do port to JPA, you'll be able to move change your implementation provider without having to make any significant changes to your codebase.
***Update & Addendum***
I checked the budget, and apparently we have the funds to reproduce the HibernateCrudRunner in this tutorial. So, here it is, if you'd like to compare it with the JpaRunner above:
package com.mcnz.model;
import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateCrudRunner {
public static void main(String[] args) {
AnnotationConfiguration config = new AnnotationConfiguration();
config.addAnnotatedClass(GameSummary.class);
config.configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Long entityId = null;
{
GameSummary gameSummary = new GameSummary();
gameSummary.setResult("win");
System.out.println(gameSummary);
HibernateCrudRunner.save(sessionFactory, gameSummary);
entityId = gameSummary.getId();
System.out.println(gameSummary);
}
GameSummary gameSummary
= HibernateCrudRunner.findByPrimaryKey(sessionFactory, entityId);
gameSummary.setResult("tie");
HibernateCrudRunner.update(sessionFactory, gameSummary);
HibernateCrudRunner.findAll(sessionFactory);
HibernateCrudRunner.delete(sessionFactory, gameSummary);
HibernateCrudRunner.findAll(sessionFactory);
}
public static void save(SessionFactory sessionFactory, GameSummary gs) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(gs);
session.getTransaction().commit();
}
public static void update(SessionFactory sessionFactory, GameSummary gs) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.saveOrUpdate(gs);
session.getTransaction().commit();
}
public static java.util.List findAll(SessionFactory sessionFactory) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Query queryResult = session.createQuery("from GameSummary");
java.util.List allSummaries = queryResult.list();
for (int i=0;i<allSummaries.size();i++) {
GameSummary gs = (GameSummary) allSummaries.get(i);
System.out.println(gs.toString());
}
session.getTransaction().commit();
return allSummaries;
}
public static GameSummary findByPrimaryKey(SessionFactory sessionFactory, Long id) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
GameSummary gs = (GameSummary) session.get(GameSummary.class, id);
session.getTransaction().commit();
return gs;
}
public static void delete(SessionFactory sessionFactory, GameSummary gs) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.delete(gs);
session.getTransaction().commit();
}
}
Cameron McKenzie is the author of Hibernate Made Easy and the Editor in Chief of TheServerSide.com
How to Learn Hibernate
Hibernate Made Easy WebSite
Video Tutorials on Learning Hibernate
Recommended Books for Learning Hibernate
Check out this updated Tutorial for Spring 3 as well.