5.3. Persistence implementation: JPA and Hibernate

The persistence modules: sportsclub-hibernate-dao and sportsclub-jpa-dao are alternative implementations of the application's persistence strategy. This means that each module will provide:
  • implementations for the repository interfaces defined in the Sportsclub-domain module;
  • Spring context definition fragments that can be reused elsewhere in the application
Effectively, the Spring configuration fragments will expose a bean implementation for each repository interface defined in the model. This means that the implementations can be swapped at build-time without any change in the business layer. This is the basis for the build process creating two different builds, each based on a different persistence implementation - including a different repository implementation jar and leaving everything else in the component stack unchanged.
Each module produces a set of beans that can be injected further into the business services of the application.

5.3.1. The Hibernate implementation

The Hibernate-based repository implementation defines a generic superclass defining all the common repository operations that the repository implementations will parametrize by specifying the entity type and primary key type.
public abstract class HibernateRepository<T, I extends Serializable> implements Repository<T, I>
{
    protected SessionFactory sessionFactory;

    Class<T> clazz;

    public HibernateRepository(Class<T> clazz)
    {
        this.clazz = clazz;
    }

    public void setSessionFactory(SessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

    protected Session getCurrentSession()
    {
        return this.sessionFactory.getCurrentSession();
    }
   
    public T findById(I id)
    {
        return (T)getCurrentSession().get(clazz, id);
    }

    public T save(T object)
    {
        T mergedInstance = (T) getCurrentSession().merge(object);
        //force a flush to refresh the id
        getCurrentSession().flush();
        return mergedInstance;
    }

    public void delete(T object)
    {
        getCurrentSession().delete(object);
    }

    public List<T> findAll()
    {
        return getCurrentSession().createCriteria(clazz).list();
    }


    public long countAll()
    {
        return (Integer)getCurrentSession().createCriteria(clazz).setProjection(Projections.count("id")).uniqueResult();
    }

    public Criteria applyRange(Criteria criteria, Range range)
    {
        return criteria.setFirstResult(range.getMinIndex()).setMaxResults(range.length());
    }
}
It is important to notice that this implementation and its subclasses are not Spring-based. The only Spring-related component of this module is the configuration which consists of the following files:
sportsclub-hibernate-dao/src/main/resources/dao-context.xml
Contains:
  • the Spring bean definitions for the repository implementations
  • the Spring-based SessionFactory definition (a LocalSessionFactoryBean), and;
  • the wiring of session factories into Spring beans.
sportsclub-hibernate-dao/src/main/resources/infrastructure.xml
Contains:
  • the definitions for infrastructure-related Spring beans, namely the data source to be used for the Hibernate SessionFactory, and;
  • the transaction manager.
Separating the infrastructure context definition file from the rest of the bean definitions allows swap between the infrastructure definition for unit testing. For example, the Hibernate SessionFactory is configured to use JTA transactions, and allows the Session to be shared with a layer of EJBs that delegate to it.

5.3.2. The JPA implementation

The JPA implementation is similar to the Hibernate implementation. It provides a parametrized superclass that is Spring-agnostic as well (except for the usage of the autowiring annotation, which can be replaced by a simple setter if any reference to Spring needs to be removed). Besides the fact that it is using the JPA API - for example, an EntityManager instead of the SessionFactory - the JPA Persistence Unit (and subsequent EntityManager) are created by the application server and not by Spring (the EntityManager is injected by Spring, but acquired from JNDI). The persistence unit is deployed from within the JPA repository jar, in order to allow the spring-domain jar to be deployed in non-JPA scenarios (for example, Hibernate) without triggering a persistence unit deployment.
The Spring application context configuration fragments are very similar to the ones encountered in the Hibernate module:
sportsclub-jpa-dao/src/main/resources/dao-context.xml
Contains the Spring bean definitions for the repository implementations, assuming an EntityManager bean is defined in the global application context definition.
sportsclub-jpa-dao/src/main/resources/infrastructure.xml
Contains the definitions for infrastructure-related Spring beans, namely the entityManger bean to be wired into JpaRepository class and the transaction manager.

5.3.3. Unit testing the repositories

Given that the infrastructure is tied tightly to the Application Platform, the repositories should be tested in isolation before integrating them with the rest of the application. If the services that the JBoss Enterprise Application Platform provides will be used at deployment time, use an embedded database, Spring's ability to create LocalSessionFactories, LocalEntityManagerFactories, and its local transaction management abilities.
To do this, use the sportsclub-test-infrastructure module, which is a test-scoped dependency. This module contains the modules used for setting up an embedded database. This is done by producing a DataSource that can be injected into the LocalSessionFactoryBean, and LocalContainerEntityManagerFactoryBean respectively. The localized SessionFactory and EntityManager defintions are located in the sportsclub-hibernate-dao, and sportsclub-jpa-dao modules, respectively.
The unit tests that are located in the respective modules use the local infrastructure files and the dao-context.xml files, as in the following example:
@ContextConfiguration(locations = 
    {"classpath:test-db-infrastructure.xml",
        "classpath:TEST-jpa-infrastructure.xml",
        "classpath:dao-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class TestJpaAccountRepository
{
   /* */
}
This configuration reuses the 'application-specific' context configuration fragment, as well as two test-specific (or local) context configuration fragments in order to create a Spring context in isolation. This way, the functionality provided by the repositories can be tested outside the running application.