Warp :: JPA integration

Warp it! Store it! Get going in seconds! new!

Wideplay's integration for the Java Persistence API (JPA)

JPA is a standard released as part of JSR-220 (or EJB3). It is roughly an analog to Hibernate or Oracle TopLink; and they are also the two most prominent vendors of JPA. The contracts to program to are a bit different from Hibernate. Warp-persist supports JPA in a vendor-agnostic fashion, similar to the Hibernate support module.

Enabling Persistence Support

To enable persistence support, configure the module when creating your injector:

Injector injector = Guice.createInjector(..., PersistenceService.usingJpa()
			 .buildModule());
 

In JPA, you specify your configuration in a persistence.xml file in the META-INF folder of that is available on the classpath (if inside a jar, then at the root for example). In this file you declare several Persistence Units that are roughly different profiles of persistence (for example to different table-spaces, or using different vendors or transaction systems). Here is a very simple JPA configuration:

<?xml version="1.0" encoding="UTF-8" ?>
<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">

<!-- A JPA Persistence Unit -->
<persistence-unit name="myFirstJpaUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
 
		<!-- JPA entities must be registered here -->
<class>com.wideplay.warp.jpa.JpaTestEntity</class>

<properties>
			<!-- vendor-specific properties go here -->
</properties>
</persistence-unit>

</persistence>

For more information on learning JPA consult this PDF excerpt from Gavin King & Christian Bauer's book Java Persistence With Hibernate. Warp-persist supports working with one Persistence Unit per injector currently. To do so, you must bind in the name of the Persistence Unit that you want to work with somewhere in your guice module:

bindConstant().annotatedWith(JpaUnit.class).to("myFirstJpaUnit");


Finally, you must decide when the persistence service is to be started by invoking start() on the PersistenceService artifact. I typically use a simple initializer class that I can bind and invoke at a time of my choosing:

public class MyInitializer { 
	@Inject MyInitializer(PersistenceService service) {
		service.start(); 
 
 		//at this point JPA is started and ready
	} 
} 

A really simple approach would be to bind MyInitializer as an eager singleton so the PersistenceService is started up when Guice starts up; but I recommend you think about how it fits into your particular deployment environment before doing so.

Session-per-transaction strategy

This is a popular strategy, where an EntityManager is created and destroyed around each database transaction. Set the transaction-type attribute on your persistence unit's configuration:

 <persistence-unit name="myFirstJpaUnit" transaction-type="RESOURCE_LOCAL">
 

And change the PersistenceModule's configuration accordingly:

Injector injector = Guice.createInjector(..., PersistenceService
			.usingJpa()
			.across(UnitOfWork.TRANSACTION)
			.buildModule());
 

Note: this is the default UnitOfWork strategy even if unspecified (as in the earlier example). For advanced users, keep in mind that in warp-persist this strategy works a bit differently from Java EE container-managed EJBs and the PersistenceContextType setting for EntityManagers is always EXTENDED (not TRANSACTION as you may be led to think). You can safely ignore this in SE or servlet environments.

Using the EntityManager inside Transactions

Once you have the injector created, you can freely inject and use an EntityManager in your transactional services:

import com.wideplay.warp.persist.Transactional;
import javax.persistence.EntityManager; 
 
public class MyService {
	@Inject Provider<EntityManager> em; 
 
	@Transactional 
	public void createNewPerson() {
		em.get().persist(new Person(...)); 
	} 
 

Note that since we are using a session-per-transaction strategy, you cannot hold a reference to the EntityManager instance across transactions (must obtain it from the provider each time). If this is not desirable, read further for the session-per-http-request strategy.

Transaction semantics

See the transactions guide for detailed information on using transactions with warp-persist and JPA.

Usage in Web Environments (session-per-http-request)

So far, we've only used a session-per-transaction strategy. In web environments this is atypical, and generally session-per-http-request is preferred (such as with the popular open-session-in-view paradigm). To enable this strategy, you first need to add a filter to web.xml:

	<filter>
		<filter-name>sessionPerRequestFilter</filter-name>
		<filter-class>com.wideplay.warp.jpa.SessionPerRequestFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>sessionPerRequestFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
 

It doesn't really matter where this filter appears (however, if you are using warp-widgets, it must appear before WidgetFilter). Then in your injector configuration:

Injector injector = Guice.createInjector(..., PersistenceService
			.usingJpa()
			.across(UnitOfWork.REQUEST)
			.buildModule());
 
 

Note that with this configuration you can run multiple transactions within the same request (and the same EntityManager or persistence context) using RESOURCE_LOCAL transactions (i.e. not JTA). This fits well with open-session-in-view.