Thursday, March 3, 2011

Persistence with JPA and Hibernate using Guice 3.0 and guice-persist

Why do I like Google frameworks? Because they are academic-like: elegant, concise, focused, open to extension so they evolve gradually and naturally. Why I don't like Google frameworks? Because they are academic-like: barely documented, lack in visual design and appearance.

Google Guice is no exception. It's been around probably as long as Spring. Anyone looking for DI framework must give it a shot. But even today you hear Spring not Guice when people talk DI.

Using JPA with Spring before I firmly decided to stick with Guice this time. I have simple back-end program: no web, no application server (PostgreSQL database and Hibernate as a JPA 1.0 provider).

I decided to take advantage of the latest JPA support in Guice: guice-persist. Guice 3.0 is required (upgrade from 2.0 if necessary) but Guice jar doesn't contain guice-persist: have both dependencies in your pom.xml when using Maven (updated):
<dependency>
  <groupid>com.google.inject</groupId>
  <artifactid>guice</artifactId>
  <version>3.0</version>
</dependency>
<dependency>
  <groupid>com.google.inject.extensions</groupId>
  <artifactid>guice-persist</artifactId>
  <version>3.0</version>
</dependency>
Without Maven simply follow your regular practices to add jars above to Java classpath.

Existing Guice configuration using module(s) need not change, but add guice-persist module when creating injector. Before:
Injector injector = Guice.createInjector(new MyAppModule());
After:
Injector injector = Guice.createInjector(new MyAppModule(), 
                          new JpaPersistModule("myapp-db"));
myapp-db is a persistence unit defined in persistence.xml placed on classpath (e.g. in Maven project it's in src/main/resources/META-INF directory):

    
        org.hibernate.ejb.HibernatePersistence
        com.example.domain.MyEntity
        true
        
            
            
            
            
            
            
            
        
    

This is JPA 1.0 persistence unit - you should be able to use 2.0 without any problems. I disabled scanning of classpath for entities using exclude-unlisted-classes: all JPA entities should be listed with class element now. The properties are specific to Hibernate and PostgreSQL.

guice-persist works via PersistService that has to be started (initialized). I do it immediately after initializing injector and using injector:

Injector injector = Guice.createInjector(new MyAppModule(), 
                          new JpaPersistModule("myapp-db"));
injector.getInstance(ApplicationInitializer.class);
and
public class ApplicationInitializer {
 @Inject ApplicationInitializer(PersistService service) {
  service.start(); 
  // At this point JPA is started and ready.

  // other application initializations if necessary
 }
}

Persistence unit defined transaction-type="RESOURCE_LOCAL" to have JPA EntityManager created and destroyed for each database transaction. Define both EntityManager and transactions in DAO class with the following Guice annotations:
public class MyAppDAO {
    @Inject
    private EntityManager em;

    @Transactional
    public MyEntity find(long id) {
        return em.find(MyEntity.class, id);
    }

    @Transactional
    public void save(MyEntity entity) {
        em.persist(entity);
    }
}
We injected EntityManager with @com.google.inject.Inject and declared transactions with @com.google.inject.persist.Transactional annotations. Now, each time find or save method called new transaction is started and committed in JPA entity manager. For more details on transaction scope (unit of work) and exception handling see this.

Wrapping it up: there is bare minimum of artifacts/configuration needed on top of standard JPA persistence.xml: new Guice persistence module (provided by guice-persist), persistence service initialization, standard Guice injection of EntityManager, and new @Transactional annotation. Of course, your mileage may vary depending on your transactional (unit of work) needs but it's hard to imagine less configuration and code when implementing data access with JPA and Hibernate.

For completeness I add a Guice module here (it needs no special configuration for guice-persist though, so I have DAO configuration here only):
public class MyAppModule extends AbstractModule {
 
    @Override
    protected void configure() {
        ...
        bind(ISomeDao.class).to(MyDao.class);
    }
}
References:
Using JPA with Guice Persist
Hibernate with JPA Annotations and Guice