Using Spring with JPA and Hibernate

Spring + Hibernate

  • HibernateTemplate is now obsolete (Spring 3.x +) – create bean for SessionFactory and wire it into Hibernate-based Repository beans
  • Use the Hibernate Session api directly
  • This approach has zero dependency on Spring APIs

How to configure:

Configure beans for your datasource, transaction manager and the SessionFactory:

<jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/>

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="jtaTransactionManager" ref="txManager"/> 
    <property name="mappingResources">
      <list>
        <value>product.hbm.xml</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <value>
        hibernate.dialect=org.hibernate.dialect.HSQLDialect
      </value>
    </property>
  </bean>

Configuring with xml files is optional if you’re using JPA annotations for your Entities instead.

Repositories using Hibernate follow same ‘new’ pattern as JDBC Template with Spring 3.x, no longer user the HibernateTemplate, inject a Hibernate SessionFactory and use this to get a Session directly. This approach doesn’t have any Spring code dependencies:

@Transactional
@Repository
public class ExampleRespositoryImpl implements ExampleRepository
{
  private SessionFactory sessionFactory;

  public ExampleRespositoryImpl(SessionFactory sessionFactory)
  {
    this.sessionFactory(sessionFactory);
  }

  public Example findExampleById(Long id)
  {
    this.sessionFactory.getCurrentSession().get(Example.class, id);
  }
}

Spring + JPA

How to configure:

Running in an EE container, configure your datasource, transaction manager and EntityManagerFactoryBean:

<jee:jndi-lookup id="dataSource" jndi-name="java:jboss/datasources/MysqlDS"/>

<bean id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/persistenceUnit"/>

If you’re running outside of an EE container, instead of looking up your datasource and container provided EntityManagerFactory, define a LocalEntityMangerFactoryBean, referencing your persistence unit name:

<bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="persistenceUnit"/>
    <property name="dataSource" ref="dataSource"/>
 </bean>

Configure your persistence.xml to reference your datasource and JPA provider (which may also be Hibernate):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit" transaction-type="JTA"><!-- for local testing use: RESOURCE_LOCAL -->
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
         <!-- KH: look up DataSource from container -->
         <jta-data-source>java:jboss/datasources/MysqlDS</jta-data-source>

        <properties>
            <!-- KH: ask JBoss to publish the EntityManager so we can reference it in the Spring config -->
            <property name="jboss.entity.manager.factory.jndi.name" value="persistence/persistenceUnit"/>

            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            <!-- value="create" to build a new database on each run; value="update" to modify an existing database; value="create-drop" means the same as "create" but also drops tables when Hibernate closes; value="validate" makes no changes to the database -->
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
            <property name="hibernate.connection.charSet" value="UTF-8"/>

        </properties>
    </persistence-unit>
</persistence>

If you’re running outside of an EE container, make the following changes:

  • transaction-type="RESOURCE_LOCAL"
  • remove the <jta-data-source> element

Write Repositories with @Repository and @Transactional, and use the @PuersistenceUnit annotation to inject the EntityManagerFactory

@Transactional
@Repository
public class ExampleRespositoryImpl implements ExampleRepository
{
  private EntityManagerFactory emf;

    @PersistenceUnit
    public void setEntityManagerFactory(EntityManagerFactory emf) {
        this.emf = emf;
    }

    public Collection loadProductsByCategory(String category) {
        EntityManager em = this.emf.createEntityManager();
        try {
             Query query = em.createQuery("from Product as p where p.category = ?1");
             query.setParameter(1, category);
             return query.getResultList();
        }
        finally {
            if (em != null) {
                em.close();
            }
        }
    }
}

This approach injects the EntityManagerFactory using @PersistenceUnit, from which you can get hold of the EntityManager. The normal approach with JPA would be to inject the EntityManager with @PersistenceContext and use the EntityManager directly.

Java EE7 Maven Dependency against Glassfish 4.x snapshot builds

Since upcoming changes and new features for EE7 are not yet published to Maven Central, you can build against the snapshot builds in the java.net repository. Add this to your pom.xml to add a reference to the java.net repo:

<repositories>
<repository>
<id>snapshot-repository.java.net</id>
<name>Java.net Snapshot Repository for Maven</name>
<url>https://maven.java.net/content/repositories/snapshots/</url>
<layout>default</layout>
</repository>
</repositories>

To add dependencies against EE7 features, add a dependency to a latest Glassfish 4.0-x snapshot build which already includes early versions for some of the new EE7 features:

<dependency>
<groupId>org.glassfish.main</groupId>
<artifactId>javaee-api</artifactId>
<version>4.0-b33</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.glassfish.main</groupId>
<artifactId>javax.ejb</artifactId>
<version>4.0-b33</version>
</dependency>

Configuring an @MessageDriven bean on JBoss AS7

If you forget to add the destination property to the activationConfig for an MDB, when deploying to JBoss AS7 you’ll get this NullPointerException. Would be better if it told you what required property was missing:

16:30:29,007 WARN  [org.hornetq.ra.inflow.HornetQActivation] (default-short-running-threads-threads - 1) Failure in HornetQ activation org.hornetq.ra.inflow.HornetQActivationSpec(ra=org.hornetq.ra.HornetQResourceAdapter@6ba59338 destination=null destinationType=javax.jms.Queue ack=Auto-acknowledge durable=false clientID=null user=null maxSession=15): java.lang.NullPointerException
    at javax.naming.NameImpl.<init>(NameImpl.java:281) [rt.jar:1.7.0_04]
    at javax.naming.CompositeName.<init>(CompositeName.java:231) [rt.jar:1.7.0_04]
    at org.jboss.as.naming.util.NameParser.parse(NameParser.java:49)
    at org.jboss.as.naming.NamingContext.parseName(NamingContext.java:440)
    at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:213)
    at javax.naming.InitialContext.lookup(InitialContext.java:411) [rt.jar:1.7.0_04]
    at org.hornetq.ra.Util.lookup(Util.java:174) [hornetq-ra-2.2.11.Final.jar:]
    at org.hornetq.ra.inflow.HornetQActivation.setupDestination(HornetQActivation.java:454) [hornetq-ra-2.2.11.Final.jar:]
    at org.hornetq.ra.inflow.HornetQActivation.setup(HornetQActivation.java:287) [hornetq-ra-2.2.11.Final.jar:]
    at org.hornetq.ra.inflow.HornetQActivation$SetupActivation.run(HornetQActivation.java:605) [hornetq-ra-2.2.11.Final.jar:]
    at org.jboss.jca.core.workmanager.WorkWrapper.run(WorkWrapper.java:212)
    at org.jboss.threads.SimpleDirectExecutor.execute(SimpleDirectExecutor.java:33)
    at org.jboss.threads.QueueExecutor.runTask(QueueExecutor.java:801)
    at org.jboss.threads.QueueExecutor.access$100(QueueExecutor.java:45)
    at org.jboss.threads.QueueExecutor$Worker.run(QueueExecutor.java:821)
    at java.lang.Thread.run(Thread.java:722) [rt.jar:1.7.0_04]
    at org.jboss.threads.JBossThread.run(JBossThread.java:122)

Here’s a correctly configured MDB:

@MessageDriven(mappedName = "queue/QueueName", activationConfig = {
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/queueName") })
public class QueueListener implements MessageListener {
...
}