Spring, DBUnit and OpenJPA
In an attempt to be economically rational I have been spending some quality time with OpenJPA. My initial impression is that its got great potential. I know Kodo had a loyal following of folks that thought the world of the software and since OpenJPA is Kodo opensourced it stands to reason that its a fairly mature bit of code. What I have discovered in my research thus far is that its cool but poorly documented. The Kodo docs are just different enough to make me crazy and Google has only a few hits that show you how to do stuff. So its early but promising.
Towards realizing that promising end I am going to start blogging bits about OpenJPA in the hope that it helps others get their heads wrapped around it and how it works. I intended to do this with all the other open JPA implementations as well (Cayenne for sure and TopLink as well assuming its not 'owned' by any single company).
So on with the show...
This example takes you through using the following bits together;
- TestNG which I like tons better than JUnit 4.x or 3.8.x
- DBUnit
- Spring - autowire tests to the beans in the context
- Maven - build the stuff and run the tests
- OpenJPA - formerly known as Kodo, ORM tool
Some time back I wrote up how to use DbUnit to test your hibernate mappings using a rather simplified financial tracking model (can that really be almost 3 years ago???) It is somewhat like the model Yahoo uses. But the model is of course not that sophistated but its purpose in life is not to unseat Yahoo from the height of fininancial management but rather to illustrate JPA modeling and the testing of the models. So with that bit of background lets take a look at the model;
I used the model unmodified (same JPA annotations) with OpenJPA and then applied the same basic approach to testing the app with DbUnit as I did before. As I tried to prove last time, having these types of tests in place is hugely useful to instill confidence in your mappings.
On to the code... The first thing is the Spring integration. The base class makes some assumptions (your test context is defined in testAppCtx.xml for example) and autowires your test to the beans defined in your context. So I can have a test for the FooService that uses and instance of that service that is injected by Spring simply by having a set method. For example, here is the bean defintion for one of the DAO's tested in this example code;
<bean id="userDao" class="net.dudney.jpaund.dao.jpa.BaseDaoJpa"> <property name="type" value="net.dudney.jpaund.domain.SiteUser" /> </bean>
Then I have Spring inject that DAO into my test class with a simple property like this;>
private BaseDaouserDao = null; ... public BaseDao getUserDao() { return userDao; } public void setUserDao(BaseDao userDao) { this.userDao = userDao; } ...
Now that my test has a pointer to the DAO I want to test I need some data to test it with, the data is captured in the form of XML for DBUnit and looks like this;
<?xml version='1.0' encoding='UTF-8'?> <dataset> <site_user id="1" version="0" username="benvolio" password="where"/> <site_user id="2" version="0" username="tybalt" password="forart"/> <site_user id="3" version="0" username="mercutio" password="thou"/> ... </dataset>
and placed into a file in the src/test/resources folder to be picked up by maven and placed into the test classpath. My test simply returns the path to the file in an overridden method like this;
@Override
public String getDataSetXml() {
return "net/dudney/jpaund/domain/data.xml";
}
The abstract DbUnitTestBase base class takes care of loading the data for you. The data is loaded before any tests are run from each class courtsey of the @BeforeClass annotation. TestNG will run the method that loads data before any other methods in your test class are run. So you can put data to test your DAO or Service in the database once and then use it in each test. If you modify the data in a way that needs to be undone you can reinitialize the database with your data by calling loadSeedData.
Something I learned also during this expierement is that the OpenJPA Maven2 Plugin is not ready for prime time. Dispite my last post on that topic I would stear clear of the M2 plugin and stick with the ant run plugin as suggest on the OpenJPA home page here.
Finally here is the code.
Good luck and I hope this is helpful in testing your JPA stuff...


Can you share your learning experience with OpenJPA Maven Plugin - what are/were the painful bits?
Would be great if you can open tickets on Codehaus JIRA.
Thanks,
Posted by Rahul on May 13, 2007 at 01:17 AM MDT #
I am trying to use te Maven plugin:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>openjpa-maven-plugin</artifactId>
<executions>
<execution>
<id>JPA Enhance</id>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
<configuration>
<toolProperties>
<property>
<name>addDefaultConstructor</name>
<value>true</value>
</property>
<property>
<name>enforcePropertyRestrictions</name>
<value>true</value>
</property>
</toolProperties>
</configuration>
<dependencies>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-persistence-jdbc</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-jdbc-5</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-kernel-5</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-lib</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
</plugin>
for your example:
http://bill.dudney.net/roller/bill/entry/20070428
But I keep getting this error:
<message>
<![CDATA[[Error while processing persistent field com.prime.tutorial.moviestore.domain.FinancialIndex.stocks, declared in C:\opt\temp\appfuse\yoursos\core\target\classes\com\prime\tutorial\moviestore\domain\FinancialIndex.class. Error details: The accessor for field getStocks in type com.prime.tutorial.moviestore.domain.FinancialIndex is private or package-visible. OpenJPA requires accessors in unenhanced instances to be public or protected. If you do not want to add such an accessor, you must run the OpenJPA enhancer after compilation, or deploy to an environment that supports deploy-time enhancement, such as a Java EE 5 application server., Error while processing persistent field com.prime.tutorial.moviestore.domain.FinancialIndex.stocks, declared in C:\opt\temp\appfuse\yoursos\core\target\classes\com\prime\tutorial\moviestore\domain\FinancialIndex.class. Error details: The accessor for field setStocks in type com.prime.tutorial.moviestore.domain.FinancialIndex is private or package-visible. OpenJPA requires accessors in unenhanced instances to be public or protected. If you do not want to add such an accessor, you must run the OpenJPA enhancer after compilation, or deploy to an environment that supports deploy-time enhancement, such as a Java EE 5 application server.]]]>
</message>
Posted by Mick Knutson on March 27, 2008 at 12:24 PM MDT #