Putting Entities to Rest with Groovy, Hibernate, and JPA
May 09, 2007
I mentioned in my previous entry about how I am totally sold on Groovy. I immediately went off and converted a couple of shell scripts that I use daily into Groovy scripts. I was pleasantly surprised by the CliBuilder for processing command-line arguments, but that is another story. What I am really excited about is what I was able to do next. While perusing Java Persistence with Hibernate, I started thinking about how Groovy might make object-relational persistence easier. I tend to dread working with Hibernate or JPA just because it takes a while to get it all setup, usually requiring the formal step of setting up a project in Eclipse.
That is where our story begins. The challenge: How hard is it to use Groovy to persist an entity class? The answer: Amazingly simple. So simple, in fact, that there is almost no reason to access the database any other way (unless you are a database purist and then you have your reasons). For me, I prefer the Hibernate or JPA route, if I can take it.
There exist a couple of other blog entries on this topic, but I promise you that I have an exciting conclusion. Let's get started.
Step 0. Groovy 1.1
You will need to be using Groovy 1.1, which has annotation support, to run these examples. At the time of writing this version is still in beta, but I have had no problems with it.
Step 1. Get the JARs
Easily the most annoying part of working with any library is having to aggregate all of the dependencies. Eventually we are going to have a clean, universal solution for this problem. Until then, Groovy helps to hide some of the nastiness. If you use Maven 2, which I happen to recommend, you will likely have most of these JARs already sitting in your local repository. Copy the list of JARs, shown below, to the ~/.groovy/lib/ folder. I have included both Hibernate and JPA JARs just so that I don't have to do this step twice. I went with MySQL for this example. If you would like to use another database, then you will need to include the appropriate JDBC driver and change the connection strings in the examples to suit that database.
- antlr-2.7.6.jar
- cglib-nodep-2.1_2.jar (fix incompatibility with Groovy asm library)
- commons-collections-3.2.jar
- commons-httpclient-2.0.2.jar
- commons-logging-1.1.jar
- dom4j-1.6.1.jar
- geronimo-j2ee_1.4_spec-1.1.jar (JTA: Java Transaction API)
- hibernate-3.2.1.ga.jar
- hibernate-annotations-3.2.1.ga.jar
- hibernate-entitymanager-3.2.1.ga.jar (Hibernate JPA)
- jboss-archive-browsing-5.0.0alpha-200607201-119.jar (Hibernate JPA)
- mysql-connector-java-5.0.5.jar
- persistence-api-1.0.jar
Please note that any JTA implementation will work. I just happen to use the Geronimo implementation since it is available in the public Maven 2 repository.
Step 2. Create an Entity class
So that I make no assumptions about existing tables, I am going to create a new entity class that will generate a table automatically. If you already have a table that you would like to use, then create an appropriate entity for it.
import javax.persistence.* @Entity @Table(name="contact") class Contact implements Serializable { @Id @GeneratedValue Long id String name String email }
Step 3. Create a SessionFactory or PersistentUnit configuration
Depending on whether you are going to do a Hibernate SessionFactory or a JPA PersistenceUnit, you will either create a hibernate.cfg.xml configuration or a META-INF/persistence.xml configuration. I will include both below. Choose the one appropriate for your choice of implementation. I will stick with the ordering of Hibernate snippets followed by JPA snippets.
<?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="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/database</property> <property name="hibernate.connection.username">username</property> <property name="hibernate.connection.password">password</property> <property name="hibernate.hbm2ddl.auto">create</property> <mapping class="Contact"/> </session-factory> </hibernate-configuration>
or
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="default" transaction-type="RESOURCE_LOCAL"> <class>Contact</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" /> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/database" /> <property name="hibernate.connection.username" value="username" /> <property name="hibernate.connection.password" value="password" /> <property name="hibernate.hbm2ddl.auto" value="create" /> </properties> </persistence-unit> </persistence>
Step 4. Startup a session and do persistence operations
In this step, we are going to do everything in one script, including starting up the Hibernate SessionFactory or JPA PersistenceUnit and then executing some persistence operations.
import org.hibernate.* import org.hibernate.cfg.* sf = new AnnotationConfiguration().configure().buildSessionFactory() sess = sf.openSession() tx = sess.beginTransaction() sess.createQuery("delete Contact").executeUpdate() sess.save new Contact(name: 'Dan Allen', email: 'dallen@example.com') tx.commit() sess.close() sf.close()
or
import javax.persistence.* emf = Persistence.createEntityManagerFactory("default") em = emf.createEntityManager() em.getTransaction().begin() em.createQuery("delete Contact").executeUpdate() em.persist new Contact(name: 'Dan Allen', email: 'dallen@example.com') em.getTransaction().commit() em.close() emf.close()
Step 5. Get groovy and create a template
Now we are going to take a pragmatic approach by creating a reusable template, which we can use to wrap the persistence operations. That way, it isn't necessary to do all the nasty work of starting up the persistence engine each time we write a script (even though it is still only a couple of lines). I call these two classes HibernateTemplate and JPATemplate respectively. They use an anonymous code block argument to a method that will execute the operations within a persistence session.
import org.hibernate.cfg.* import org.hibernate.* class HibernateTemplate { SessionFactory sf Session sess HibernateTemplate(config) { if (!config) config = "/hibernate.cfg.xml" sf = new AnnotationConfiguration().configure(config).buildSessionFactory() } void doInSession(c, shutdown) { sess = sf.openSession() def tx = sess.beginTransaction() c(sess) tx.commit() sess.close() if (shutdown) shutdown() } void shutdown() { sf.close() } }
or
import javax.persistence.* class JpaTemplate { EntityManagerFactory emf EntityManager em JpaTemplate(pu) { if (!pu) pu = "default" emf = Persistence.createEntityManagerFactory(pu) } void doInEntityManager(c, shutdown) { em = emf.createEntityManager() em.getTransaction().begin() c(em) em.getTransaction().commit() em.close() if (shutdown) shutdown() } void shutdown() { emf.close() } }
Step 6. Take advantage of the templates
Now I will take the code from Step 4 and rewrite it using the template objects.
new HibernateTemplate().doInSession({ sess -> sess.createQuery("delete Contact").executeUpdate() sess.save new Contact(name:'Dan Allen', email: 'dallen@example.com') }, true)
or
new JpaTemplate().doInEntityManager({ em -> em.createQuery("delete Contact").executeUpdate() em.persist new Contact(name:'Dan Allen', email: 'dallen@example.com') }, true)
Look at that! I am using 4 lines of code to clean out the table and persist an entity!! Now that is groovy! If that doesn't get you excited about Groovy, then I don't know what will.
13 Comments from the Peanut Gallery
1 | Posted by zeratul on May 12, 2007 at 01:23 PM EST
Nice article, but i got a question, i'd like to know if i can mix both hibernate and JPA, using a Persistence Provider for Hibernate and then use JPA for accesing Hibernate methods or something like that. Im not sure if using Hibernate Persistence Provider I can use JPA methods for accesing DB. Thanx for your answer.
2 | Posted by Dan Allen on May 14, 2007 at 12:09 AM EST
The Hibernate EntityManager implements the programming interfaces and lifecycle rules as defined by the JPA specification. This wrapper implements a complete JPA solution on top of the mature Hibernate core. In turn, the JPA EntityManager interface allows access to the underlying persistence provider via the EntityManager#getDelegate() method. Therefore, the answer to your question is, yes, you can use either the JPA interfaces or the native Hibernate interfaces when Hibernate is being used as the persistence provider for JPA.
I highly recommended the Java Persistence with Hibernate book by Manning if you have additional questions about Hibernate and JPA. That book answers just about any question that you encounter using these tools. Trust me, it is very comprehensive.
3 | Posted by Cedric on June 12, 2007 at 11:16 AM EST
Hi,
Nice article. I was trying to do something close to that when I found your post. I'm having troubles doing the same.
First, I assume you used the latest groovy release (1.1 with annotations support) since it is not explicit.
Then, I'm trying to do that using an embedded Groovy engine. The simple application I've made just creates a GroovyShell which runs the main script. All goes fine until Hibernate reaches the entity mapping in the XML file : it fails with a "unable to load class declared as ..." exception, although Groovy finds my entity bean.
Any idea ?
4 | Posted by Dan Allen on June 13, 2007 at 11:34 AM EST
Unfortunately, I'm not sure I could do any better than you in debugging the problem. My experience with Groovy has been brief.
My advice is to grab the Groovy plugin for Eclipse and then fire up the Eclipse debugger. That should give you more insight into the context held by the GroovyShell and perhaps bring to light the problem. However, if the script works fine without the embedded shell, it sounds like a possible bug in Groovy.
By the way, I added the required Groovy version as Step 0 of this post.
5 | Posted by siegfried on August 01, 2007 at 01:36 AM EST
I'd love to see this code work!
Would the author care to zip up his jar files and sample code and allow us to download it? I'm having trouble finding all those jar files -- even using maven. Thanks, Siegfried
6 | Posted by Antony Stubbs on October 07, 2007 at 05:05 AM EST
"Would the author care to zip up his jar files and sample code and allow us to download it?"
+1
7 | Posted by Dan Allen on October 19, 2007 at 03:17 PM EST
I have put the task of making these files available on my agenda. Hopefully I will have it out somewhere in a day or two.
8 | Posted by Rafael Nami on January 03, 2008 at 09:20 PM EST
Excellent tutorial about Groovy!!!. I'm sold out for Groovy for weeks, but after reading this post, I'd got to try it!!! So, I've used Maven2 and Eclipse Groovy plugin to have this working, and it worked like a charm!!!! Thanks a lot for this tutorial, and Best Regards
9 | Posted by boardtc on June 05, 2008 at 05:22 AM EST
I am wondering where to store these entities and how to put all this code together. Where you able to package up the code as hoped?
10 | Posted by Dan Allen on June 05, 2008 at 10:47 AM EST
Unfortunately, I have been *very* busy with Seam in Action lately and haven't been able to work on making code from the blog available. I have highlighted this item on my TODO list, so hopefully I will get to it soon.
As for the question "where to store these entities?", I guess my response would be "in a database". I'm not sure what you are looking for here.
11 | Posted by boardtc on June 06, 2008 at 12:14 PM EST
Thanks. I was not sure where to put all the above files..studying http://www.hibernate.org/hib_docs/v3/reference/en/html/tutorial.html - made your post more understandable. I got hibernate with annotations working with java classes but getting things to work with groovy is harder. I would like to use GORM standalone http://docs.codehaus.org/display/GRAILS/GORM+-+StandAlone+Gorm but unfortunately it has not been updated for grails 1....
12 | Posted by Oleh on June 25, 2008 at 06:13 PM EST
Did anybody try to use properties (not fields) mapped for Groovy class?
I have org.hibernate.MappingException: Could not determine type for: groovy.lang.MetaClass, for columns: [org.hibernate.mapping.Column(metaClass)]:
I tried to override getMetaClass method with @Transient annotation. No effect.
MetaClass is considered persistable property by Hibernate. How can tell him otherwise?
Any ideas?
13 | Posted by boardtc on July 02, 2008 at 09:10 AM EST
got there in the end with groovy & hibernate thanks to your post! Note, to load a class one can do session.load(Contact.class...