22 July 2008

Spring, Annotations and JPA web project

I struggled to set up the latest version of Spring (2.5.4) with JPA using annotations as much as possible for a web project (not using spring MVC). I could find much documentation and the documentation I found was lacking and not specific to the setup I was trying. So here is how I did it for those who need to know.

Project Setup

I'm using Netbeans, but it shouldn't matter what you are using - I'll keep the IDE out of the description and use external libraries.

You will need a JPA provider (I'm using hibernate 3.2), Spring libraries and a database (I'm using PostgreSQL). I am also using JUnit4.4, which makes testing with spring a lot easier, I have to edit the testing libraries manually on Netbeans 6.1, as it comes standard with JUnit4.1.

So, create a web project.

Setting up JPA

Create a database first, I created one called 'deleteme' with user 'deleteme' and password 'deleteme', so that I will remember to delete it. At some stage.
with your IDE and create a database.

Next, create a 'persistance.xml' file in the 'WEB-INF/classes/META-INF' directory, this is what myne looks like:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
  <persistence-unit name="deleteme" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
    </properties>
  </persistence-unit>
</persistence>


Note: The database details are not in the persitance config file, they will be in the spring config file (more on that later). The only thing you need change is the persistence-unit name which in my case is, 'deleteme'.

Setting up Spring

Create a new file 'spring.xml' (for example) and add the basic configurations:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
          
    <context:annotation-config/>
    <context:component-scan base-package="com.gamatam.example" />
   
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.postgresql.Driver"/>
        <property name="url" value="jdbc:postgresql://localhost/deleteme"/>
        <property name="username" value="deleteme"/>
        <property name="password" value="deleteme"/>
    </bean>
   
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>
   
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
   
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
   
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
   
    <tx:annotation-driven />
</beans>


Note: I setup the datasource in the spring configuration, as the configuration is more or less in one place and it makes unit testing easier (rather than using a datasource from the J2EE container).

The different namespaces needed tripped me up the first time I set this up, its a great idea, but does create more confusion to new users.

Basically, I setup a datasource for the database and an EntityManagerFactoryBean, which will find the persistance configuration. Then add the exception translation and transaction management and specify that the config is annotation driven. Thats it!

First Test

Now you can create your first test to see that the configuration files are being loaded OK etc.. Here is myne:

package com.gamatam.example;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;

/**
 *
 * @author brians
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:web/WEB-INF/spring.xml" })
public class Basics {
   
    @Test
    public void first(){
       
    }
   
    public Basics() {
    }

    @BeforeClass
    public static void setUpClass() throws Exception {
    }

    @AfterClass
    public static void tearDownClass() throws Exception {
    }

}


Note: With the 'RunWith' and 'ContextConfiguration' annotations, testing spring becomes a breeze, no more excuses :(

If that is working for you, you are past the worst part of your project and can go on to code.

Whats Next?

Well, next create an entity bean and test it, create a DAO for it and test it, create your business/service layer with transactinos and test it. That will be in part two..

This is what worked for me, you may not like it but comments on it anyway, I'd love to here from you.

Brian