Dec 11, 2009

Simplifying JPA Testing with Spring’s PersistenceUnitPostProcessor

At my current client, we are writing an application that is deployed as an ear into Websphere. One of the goals of the project is to write out-of-container tests so that we don’t have to build and deploy over and over again. Two of the tools we are using in this application is JPA for the ORM and Spring. Initially, we had two persistance.xml files, one for production and one for testing. Some of the differences were that the production version used a transaction type of JTA and the test version used resource local. There were a few other property differences, but otherwise they needed to be identical. At first this was ok, but as our list of classes and properties grew it became a maintenance hassle.

At this point we wanted to find a way to only have one persistance.xml to use in both prod and testing. After some research we found Spring’s PersistenceUnitPostProcessor interface. Implementing this interface allows you to make changes to the persistence unit that is defined in persistence.xml. There is one method that looks like this:

public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui);

The MutablePersistenceUnitInfo object that is passed in has all the information in persistence.xml in it and can be altered as needed. Here’s how we got it to work. Here’s how our production perstence.xml looks:

<?xml version="1.0"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"
   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="oracleOpenJpa" transaction-type="JTA">
      <jta-data-source>jdbc/dS</jta-data-source>
      <non-jta-data-source>jdbc/dS-NO-JTA</non-jta-data-source>

      <class>...</class>
      <exclude-unlisted-classes>true</exclude-unlisted-classes>

      <properties>
         <property name="openjpa.ConnectionFactoryMode" value="managed" />
         <property name="openjpa.TransactionMode" value="managed" />
      </properties>

   </persistence-unit>
</persistence>

Here is our implementation of the interface:

package org.acme.jpa;

import java.util.Map;
import java.util.Properties;

import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;

import org.apache.commons.lang.ObjectUtils;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;

public class MyPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {

   private String persistenceUnitName;
   private Properties persistenceProperties;
   private DataSource dataSource;

   public MyPersistenceUnitPostProcessor(final String persistenceUnitName, 
          final DataSource dataSource, final Properties persistenceProperties) {
      this.persistenceUnitName = persistenceUnitName;
      this.persistenceProperties = persistenceProperties;
      this.dataSource = dataSource;
   }

   public void postProcessPersistenceUnitInfo(final MutablePersistenceUnitInfo pui) {
      if(ObjectUtils.equals(persistenceUnitName, pui.getPersistenceUnitName())) {
         pui.setJtaDataSource(null);
         pui.setNonJtaDataSource(dataSource);
         pui.setTransactionType(PersistenceUnitTransactionType.RESOURCE_LOCAL);
         pui.setPersistenceProviderClassName(
            "org.apache.openjpa.persistence.PersistenceProviderImpl");

         final Properties properties = pui.getProperties();

         for (final Map.Entry entries : persistenceProperties.entrySet()) {
            properties.put(entries.getKey(), entries.getValue());
         }
      }
   }

}

And here’s the spring configuration:

<bean id="entityManagerFactory" 
   class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
   <property name="persistenceUnitName" value="oracleOpenJpa" />
   <property name="dataSource" ref="oracleDataSource" />
   <property name="persistenceUnitPostProcessors">
      <list>
         <bean class="org.acme.jpa.MyPersistenceUnitPostProcessor">
            <constructor-arg value="oracleOpenJpa" />
            <constructor-arg ref="oracleDataSource" />
            <constructor-arg>
               <props>
                  <prop key="openjpa.ConnectionFactoryMode">local</prop>
                  <prop key="openjpa.TransactionMode">local</prop>
               </props>
            </constructor-arg>
         </bean>
		
      </list>
   </property>
</bean>

As you can see in the implementation, we set the JTA datasource to null (we don’t use it for testing), and set the non-jta datasource to the one defined in Spring. We also set the transaction type to resource local, set the provider class name and add whatever properties we pass in. In our case we just set a couple properties to “local”. By using these tools, we are able to just keep one persistance.xml file for both production and for testing and the maintenance hassle is gone.

About the Author

Object Partners profile.
Leave a Reply

Your email address will not be published. Required fields are marked *

Related Blog Posts
Android Development for iOS Developers
Android development has greatly improved since the early days. Maybe you tried it out when Android development was done in Eclipse, emulators were slow and buggy, and Java was the required language. Things have changed […]
Add a custom object to your Liquibase diff
Adding a custom object to your liquibase diff is a pretty simple two step process. Create an implementation of DatabaseObject Create an implementation of SnapshotGenerator In my case I wanted to add tracking of Stored […]
Keeping Secrets Out of Terraform State
There are many instances where you will want to create resources via Terraform with secrets that you just don’t want anyone to see. These could be IAM credentials, certificates, RDS DB credentials, etc. One problem […]
Validating Terraform Plans using Open Policy Agent
When developing infrastructure as code using terraform, it can be difficult to test and validate changes without executing the code against a real environment. The feedback loop between writing a line of code and understanding […]