Persistence Made Easy with Groovy and JPA

I was recently looking into using JPA (Java Persistence API) in desktop applications to persist the UI’s state. After all, that’s what JSR 296‘s reference implementation relies on to save the frame’s size and position. There are several JPA implementations available out here and I have chosen TopLink from Oracle, as it comes bundled with NetBeans 5.5.

Using JPA in a sample Java application proved to be very easy, particularly with NetBeans wizards and editors. No need to worry about writing the POJOs by hand, no need to worry about writing the XML configuration file, everything is handled for you. And I like that.

What struck me though is how clean and easy the code looks. Yet, I wanted something even easier and cleaner so I tried to use JPA/TopLink with Groovy. I encountered a few issues but thanks to Doug Clarke, Alexandru Popescu and Thomas Risberg I finally have something that works.

The following example requires Java DB (or Apache Derby as you prefer), Oracle TopLink Essentials and a snapshot of Groovy 1.1. To install Groovy 1.1, first install Groovy 1.0 by following the instructions from the website, remove groovy-1.0.jar from the lib/ directory and replace it with groovy-all-1.1-SNAPSHOT.jar. Groovy 1.1 is optional but brings support for annotations, thus avoiding some XML mess.

The full source code of this example, along with Java DB and TopLink, is available for download.

The very first step to use JPA with Groovy is to write your entity bean that you will persist to the database and later retrieve. Being very original, I create the following Person class. The annotations on the id field tell JPA to auto-generate an ID column in the database. Here is the code of Person.groovy:

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id

@Entity
class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id

    String firstName
    String lastName
}

Because we are using Groovy, the getters, setters and constructors are automatically generated for us. This bean can now be used to persist data in the database, as the following script, PopulateAddressBook.groovy demonstrates:

import javax.persistence.EntityManager
import javax.persistence.EntityManagerFactory
import javax.persistence.Persistence

def factory = Persistence.createEntityManagerFactory("AddressBookStore")
def manager = factory.createEntityManager()

manager.getTransaction().begin()

manager.persist new Person(firstName: "Alexis", lastName: "Moussine")
manager.persist new Person(firstName: "Ludovic", lastName: "Champenois")
manager.persist new Person(firstName: "Francois", lastName: "Orsini")
manager.persist new Person(firstName: "Eric", lastName: "Mahe")
manager.persist new Person(firstName: "Roman", lastName: "Strobl")
manager.persist new Person(firstName: "Tor", lastName: "Norbye")
manager.persist new Person(firstName: "James", lastName: "Gosling")
manager.persist new Person(firstName: "Chet", lastName: "Haase")
manager.persist new Person(firstName: "Richard", lastName: "Bair")

manager.getTransaction().commit()

This script cannot be run yet as you need to configure the persistence unit that describes the data store. Persistence units are defined in a file called META-INF/persistence.xml. In the example above I have defined a persistence unit called AddressBookStore:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" 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">
    <persistence-unit name="AddressBookStore" transaction-type="RESOURCE_LOCAL">
        <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
        <properties>
            <property name="toplink.jdbc.url" value="jdbc:derby:address-book;create=true" />
            <property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
            <property name="toplink.jdbc.user" value="app" />
            <property name="toplink.jdbc.password" value="app" />
            <property name="toplink.ddl-generation" value="create-tables" />
            <property name="toplink.application-location" value="./db-schema"/>
        </properties>
    </persistence-unit>
</persistence>

As you can see, this file contains several properties specific to TopLink. You can refer to the online documentation to understand the meaning of each of them. However, it is important to note that this configuration requires to have a folder called db-schema/ in the execution directory. You can either create it or remove the property toplink.application-location.

We also need to tell JPA what beans we want to persist. When using annotations, this is usually done in persistence.xml by adding a <class /> element containing the name of your bean. Unfortunately, some class loading conflict between TopLink and Groovy prevent us from doing so. The solution is to use another configuration file called META-INF/orm.xml. This file can be used to override the beans annotations. It can also be used to describe the persisting field and get rid of the annotations altogether. This is what you want to use with Groovy 1.0 which does not support annotations. In our case, however, we simply need to declare the name of our bean:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
     http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">
    <entity class="Person" name="Person" metadata-complete="false" access="FIELD">
    </entity>
</entity-mappings>

You can now run the script to populate the database with a few entries:

groovy -cp ../lib/derby.jar:../lib/toplink-essentials.jar:. PopulateAddressBook.groovy

If you encounter an error, check that you are using Groovy 1.1 and that both XML configuration files are correctly set up. When everything runs smoothly, you can proceed to the last step, querying the database.

The code to query the database and bring back POJOs is surprisingly short. The following asks for the list of all persons and print them by descending order of first names:

import javax.persistence.EntityManager
import javax.persistence.EntityManagerFactory
import javax.persistence.Persistence
import javax.persistence.Query

def factory = Persistence.createEntityManagerFactory("AddressBookStore")
def manager = factory.createEntityManager()

def personQuery = manager.createQuery("SELECT p FROM Person p ORDER BY p.firstName DESC")
personQuery.getResultList().each { println "${it.firstName} ${it.lastName}" }

Instead of writing our query in this snippet, we can move it to the bean itself and give it a name:

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.NamedQueries
import javax.persistence.NamedQuery

@Entity
@NamedQueries(value = [
    @NamedQuery(name = "Person.findByFirstName",
                query = "SELECT p FROM Person p WHERE p.firstName = :firstName")
])
class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id

    String firstName
    String lastName
}

The bean now contains a query named Person.findByFirstName which accepts one parameter called firstName. The following snippet shows how to use it to list all of the persons whose first name is “Alexis:”

personQuery = manager.createNamedQuery("Person.findByFirstName")
personQuery.setParameter("firstName", "Alexis")
personQuery.getResultList().each { println "${it.firstName} ${it.lastName}" }

I hope this example made you want to use Groovy and JPA together. Remember you can download the full example.

26 Responses to “Persistence Made Easy with Groovy and JPA”

  1. Nice example Romain, thanks for making it available for all to see. Keep on Groovying!

  2. javi says:

    wow!!! nice work, thanks for the article!

  3. Mark Menard says:

    Hi Romain,

    I’ve been thinking about using Groovy with JPA for a while now. I just hadn’t gotten around to setting up a Groovy dev environment so I could compile 1.1. So, thanks for the 1.1 snapshot jar. I’m going to try this out with my Groovy Works and see how it works.

    Thanks,

    Mark

  4. james says:

    Hi,

    May I know what is your reason that you prefer toplink over hibernate? :)

    Thanks
    james

  5. Romain Guy says:

    Mark, the first time I tried to use JPA, I went with Hibernate. The documentation was so large and complex I couldn’t even find a small example like this to make JPA works with Java SE. Also, TopLink is made of one JAR, not a gazillion like Hibernate (gosh, do I hate all those Jakarta dependencies @#!). Last but not least, TopLink is used by NetBeans when you create a persistence unit.

  6. aron-smith says:

    Hmm, I do so prefer seeing POJO versus guessing what side effects will come with writing annotations and scripts – regardless of how groovy or trendy it may be.

    While it was not love at first sight for Hibernate, we now have a bond that is strong enough to go public with :-)

    With Hibernate I have many, many classes – POJO beans – that I throw over the wall to be persisted or retreived with 0 additional code, so it certainly meets my needs.

    Hibernate may have more jars (I use a lot of the Jakarta libs rather than rolling my own) but Hibernate=1 TopLink+Groovy=2

    Each to their own of course

  7. Romain Guy says:

    aron, you are not comparing things equal here. I’m not advocating using Groovy necessarily, I’m just showing how you can use it with JPA. Ergo, TopLink = 1 JAR and Hibernate is still a gazillion ;-) And without annotations you just have tons of XML. I won’t fight for any of these solutions, I know which I prefer and that’s all I care about :p

  8. Cool, you can actually wire this using Spring 2.0 and have your implementation in Ruby while your interface is Java.

    BTW, james. You should be asking why would I choose Hibernate over TopLink instead of the other way around.

  9. If you want to use Hibernate it’s very easy. Just modify the WEB-INF/persistence.xml file to use org.hibernate.ejb.HibernatePersistence as the provider and add the Hibernate specific properties to that file as well. Then add the 10 jars that you need to the classpath. _NO_ changes to the application code is necessary. This is the beauty of JPA – pluggable providers. You get to choose what you like without tying your code to your choice.

  10. Romain Guy says:

    Thomas, that’s what I like about JPA. A couple of people sent me emails telling me to try out db4o, an object database, which I did try a few years ago. My main problem with using such a library is that you cannot replace it by another one easily (you can still write your own data access layer, but it’s so much work… ;-)

  11. Hello Romain, i wont comment on the Groovy stuff. For me JPA is a good step forward regarding java and database development.Thouhg I would like to see swing following all these major updates. I am taling about the GUI binding stuff (see Datasets – Master – Detail GUI stuff) available in other platforms for years now. I know that was for some time a project called JDNC (Java Database network compoments) its now subproject of a bigger Swing utils project (cant remember the name). Such compoments are very vital for any decent modern Swing aplpication and still we are waiting to be standard – in some way!

    its good tha people like you, which lead the Swing front – you are mentioning JPA and other real world examples – apart from the funcy graphics and filthy rich clients..

    please keep posting about Swing from this perspective as well

    thanks for your time

    Greetings from Athens, greece

  12. Romain Guy says:

    Paris: A standard for bindings is coming. Check out JSR 295 (and 296 for that matter.)

  13. Christiaan says:

    Hi Romain,
    yes it’s great isn’t it! I myself work on a rich client java project for a couple of years now. When we started the project we decided to go for Java (and not .Net) because of the persistence spec JDO. In combination with Jide’s component framework we were able to show some really nice things in short time. We have now developed quite a solid framework to handle tasks and written generic components to deal with every day issues you encounter in a rich client application which made the start up of this project difficult (I am curious whether jsr296 provides similar solutions, so I need to take a look at it soon). Btw, thanks for your various Java2D tips, I’ve embedded a couple of them in our splash screen;-)

    kind regards,
    Christiaan

  14. Ramon says:

    One thing that could show more easyness is the use of a groovy category and then say:
    use(PersistencCategory.class){
    new Person(firstName:“Alexis”,lastName: “Moussine”).persist()
    //… and so on
    }

  15. Richard says:

    Romain,

    Thanks for this blog! Out of interest, what are your thoughts, as a developer of client-side apps, that shipping Groovy with your app will require an extra megabyte (or so) of Groovy JARs?

    As it is now, whilst Groovy does compile down to pure Java .class files, they still have a dependency GroovyObject.

    I have raised this with the Groovy team http://jira.codehaus.org/browse/GROOVY-1818 but they may need someone more influential (such as yourself) to make the case :)

  16. Romain Guy says:

    I actually don’t mind that much. Now, I never had to deploy applications at a large scale where size did matter. But when I see the size of most of the apps I download and install, I don’t really care about 1 MB. Especially when I deem it useful to me, and therefore to the user. I am more concerned about libraries that come with hundreds of dependencies (for instance, any Jakarta library :-) like log4j, common-login or even junit.

  17. Ivan Dolvich says:

    It’s a very nice example. I’m playing with it now, but I couldn’t make a @OneToMany relationship work. I think I need something like “Set persons” here. Is this possible with Groovy 1.1, since it doesn’t support Generics yet? Greetings, Ivan

  18. olak says:

    Hi, nice I agree. I’m working with Hibernate and JPA is nice. But what annoys me a bit is that you have to put in the name of the fields as strings, like you did in your query:

    query = “SELECT p FROM Person p WHERE p.firstName = :firstName”)

    if you change the name of the property, you have to be certain to change it in these strings as well, would be nice if it was possible to specify the fields directly!

  19. Romain Guy says:

    IDEs like IntelliJ IDEA will take care of that when you do a refactoring.

  20. Vadim Voituk says:

    Thanks for good post.
    I’ll recomend it as good intro into power of Groovy for newbies

  21. Rudolf Pfahler says:

    Romain gz, good woork. Thats what ive been looking for. It works fine, but if i try it with java-embedded-groovy the class Person cannot be found. Have you any idea?

  22. Fred Janon says:

    Thanks for the good work. For the Windows users, the -cp option needs “;” as a separator, not “:” as for Linux. It would be nice if you could include the batch files for Windoz:

    populate.bat:
    groovy -cp ../lib/derby.jar;../lib/toplink-essentials.jar;. PopulateAddressBook.groovy

    query.bat

    groovy -cp ../lib/derby.jar;../lib/toplink-essentials.jar;. QueryAddressBook.groovy

    Just tried it with Groovy Version: 1.5.4 JVM: 1.6.0_02-b06, the example works great.

  23. Roger Lovelock says:

    Currently a newbie with Groovy, but trying to implement JPA in a test app with Netbeans. I have downloaded the source etc and can run it fine from the command line – but I want to set it up as a Netbeans Project so that I can work in an IDE to make mods etc etc. Have tried all sorts of ways without success – but my impression was that the whole thing started off as a Netbeans Project.
    Sort of problem I have is Netbeans looking for a class to run when I try ‘run file’ on the populate script. Any help appreciated!