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.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]