Maven and JPA Programming

It cannot be overstated just how helpful it is to have a lightweight persistence system in your testing configuration. The problem with JPA annotations like @PersistenceUnit for dependency injection is that a lot of your primary code can only be run from within a full-fledged J2EE application server.

I wanted to take a moment to rant a little about Maven and JPA programming. If you haven’t switched to the Maven way of things (It took me years to give into the dark side!) or you just started doing Maven-ized projects recently, you might still be getting the hang of things.

I won’t lie: it can be relatively hard to get used to. For example, take a look at the traditional directory structure for your project:

standard Maven directory layout

This may look a bit complex (like the first time you looked at a Unix directory structure!?) but in fact it’s a fantastic convention. For example, it’s so amazingly easy for me to create sample data files, drop them into src/test/resources/sampleData and then pull the data will a quick Unit Test using

InputStream sampleData = getClass().getResourcesAsStream("/sampleData/myFile");

and away you go!

But here’s the other fantastic thing: you can use a customized persistence.xml configuration solely dedicated to your tests and testing environment. This is incredibly powerful because you can set things up to use the standard JNDI-defined datasources in your project (which require the full-fledged application server environment) but in your testing environment you can set up a “standalone” configuration that eschews the JNDI complexity in exchange for a vanilla DataSource that is defined by explicitly declaring the JDBC driver.

In other words, my product environment’s persistence.xml looks something like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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_2_0.xsd">
  <persistence-unit name="production" transaction-type="RESOURCE_LOCAL">
    <description>Lookup Tables and Experiment Configurations for the ADI-LP Services</description>
    <non-jta-data-source>java:jboss/datasources/PRODUCTIONDS</non-jta-data-source>
    <class>foo</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <validation-mode>AUTO</validation-mode>
    <properties>
    	<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
    	<property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
    	<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
    </properties>
  </persistence-unit>
</persistence>

but you can have an alternate persistence.xml defined in your test directory that looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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_2_0.xsd">
  <persistence-unit name="testing-h2" transaction-type="RESOURCE_LOCAL">
    <description>Persistence using a built-in memory-only H2 (Hyperion) database</description>
    <class>foo</class>
    <class>experimental</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <validation-mode>AUTO</validation-mode>
    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
      <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
      <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
      <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
    </properties>
  </persistence-unit>
  <persistence-unit name="testing-psql" transaction-type="RESOURCE_LOCAL">
    <description>Persistence using my (production) PostgreSQL local database</description>
    <class>foo</class>
    <class>experimental</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <validation-mode>AUTO</validation-mode>
    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
      <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
      <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
      <property name="hibernate.temp.use_jdbc_metadata_defaults" value="false"/>
      <property name="javax.persistence.jdbc.url" value="jdbc:postgresql:testing"/>
      <property name="javax.persistence.jdbc.user" value="me"/>
      <property name="javax.persistence.jdbc.password" value="sdfsdf9"/>
    </properties>
  </persistence-unit>
</persistence>

If you look closely, you’ll see some of the cool things I’m doing.

  • I have a H2 (Hyperion) datasource that I use by default in my Unit Tests. That means I can throw my code on a computer, run “mvn test” and have my unit tests run without the need of any local database system!
  • I also have an alternate testing-psql persistence unit defined that uses my more traditional PostgreSQL database. (The one I’m using in production.) That way I can run a quick unit test and closely examine the exact schema (including indices and column types) that Hibernate is defining in Postgres for me.
  • I explicitly declare JPA classes  in my persistence.xml files so that I can start playing around with some partially completed classes (where I haven’t figured out the JPA annotations right yet). Note that the <class>experimental</class> is only defined in the testing configurations. Once I get things working out right, I’ll move them up to the primary persistence.xml file.

It cannot be overstated just how helpful it is to have a lightweight persistence system in your testing configuration. The problem with JPA annotations like @PersistenceUnit for dependency injection is that a lot of your primary code can only be run from within a full-fledged J2EE application server. Yeah, Eclipse can run things like JBoss pretty easily, but you still have to flesh-out the framework of your application before you start writing your model classes.

This dual-configuration allows me to define a utility “JPAResources.java” class that uses the “standalone” approach of creating an EntityManagerFactory first, and from that creating an EntityManager.

public class JPAResources {

	private static EntityManagerFactory emf;

	protected static EntityManagerFactory getEntityManagerFactory() {
		try {
		if ((emf==null) || (! emf.isOpen())) {
			// change to "testing-psql" to engage my local PostgreSQL DB
			emf = Persistence.createEntityManagerFactory("testing-h2");
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
		return emf;
	}
}

Et voila! I just have my Unit test use this to grab the EntityManager during the @BeforeClass stage and save it to a static private variable for the individual tests to use. It’s almost as good as the fancy new dependency injection that you’ll have when you deploy your code to the production environment.

Yes, you can try to use a testing runner like Arquillian to get the full-fledged JPA capability. But even then, you actually can’t use a lightweight container of any type to mimic the @PersistenceUnit injection. Arquillian can use an embedded GlassFish server, but that’s starting to get painful. The thing I like about this trick is that it’s lightweight and relatively platform/configuration independent. And the biggest advantage is that you can start writing your model classes quickly.

And that was the whole point to the POJO revolution, wasn’t it!?

By the way, the hardest part of the whole configuration may be getting your Maven dependencies figured out right. I’m using the JBoss 7.1.1 application server, so I chose to mirror the exact same versions of Hibernate that are found in JBoss. I probably put the most time into carefully planning my dependencies so I kept things simple…

  <dependencies>
    <dependency>
      <groupId>org.jboss.spec</groupId>
      <artifactId>jboss-javaee-6.0</artifactId>
      <version>3.0.0.Final</version>
      <type>pom</type>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>(4.8,5.0)</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-all</artifactId>
      <version>[1.8,)</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>(1.3.160,1.4)</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>9.1-901-1.jdbc4</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.0.1.Final</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>4.0.1.Final</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>4.2.0.Final</version>
      <scope>provided</scope>
    </dependency>
  <dependencies>

 

Author: Murray Todd Williams

I live in Austin, Texas. I'm enthusiastic about food, wine, programming (especially Scala), tennis and politics. I've worked for Accenture for over 12 years where I'm a Product Manager for suite of analytical business applications.

Leave a Reply

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