Solutions to IT problems

Solutions I found when learning new IT stuff

Spring 3 with JPA 2.0 (Hibernate) for beginners

leave a comment »


Introduction

This is a follow-up post to Spring 3 and Hibernate 4 for beginners.

After some considerations I decided to change my application to use JPA instead of “native Hibernate”. The configuration here assumes a standalone application, meaning outside of application container like Tomcat. However changing to JNDI and JTA is “only” a matter of configuration.

Changes

Entity Classes

If you used JPA annotations for your entity classes you don’t have to change anything. If you want to be fully independent of the JPA Implementation you will need to remove all Hibernate specific annotations which can be problematic in certain cases.

Abstract DAO

This remains almost the same. You need to exchange the sessionFactory with entityManager and thats it.

@Repository
public abstract class AbstractJpaDAO< T extends Serializable> {

    private Class< T> clazz;
    @PersistenceContext
    private EntityManager entityManager;

    public AbstractJpaDAO(final Class< T> clazzToSet) {
        this.clazz = clazzToSet;
    } 

    public T getById(final Long id) {
        Preconditions.checkArgument(id != null);
        return getEntityManager().find(clazz, id);
    }

    public List< T> getAll() {
        return getEntityManager().createQuery("from " + clazz.getName())
                .getResultList();
    }

    public void create(final T entity) {
        Preconditions.checkNotNull(entity);
        getEntityManager().persist(entity);
    }

    public T update(final T entity) {
        Preconditions.checkNotNull(entity);
        return (T) getEntityManager().merge(entity);
    }

    public void delete(final T entity) {
        Preconditions.checkNotNull(entity);
        getEntityManager().remove(entity);
    }

    public void deleteById(final Long entityId) {
        final T entity = getById(entityId);
        Preconditions.checkState(entity != null);
        delete(entity);
    }

    /**
     * @return the entityManager
     */
    public EntityManager getEntityManager() {
        return entityManager;
    }

    /**
     * @param entityManager the entityManager to set
     */
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }
}

Configuration

First you will not need to create a persistence unit! You can but since spring 3.1 this is not required anymore. If you are using Netbeans and see the warning “The Project does not contain a persistence unit” you can safely ignore it.

Below the new Spring Application-Context file. The important changes are replacing session factory with entity manager factory and changing transaction manager to jpa transaction manager. The datasource and connection pool remains exactly the same.

I’ve also added Service classes to the configuration which I created after writing the previous post.

<?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:aop="http://www.springframework.org/schema/aop"
       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/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">      

    <context:component-scan base-package="org.bitbucket.kienerj.moleculedatabaseframework" />
    <context:annotation-config />    
    
    <bean id="entityManagerFactory" autowire="autodetect"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- This scan the packages for entity classes an hence no need for persistence unit -->
        <property name="packagesToScan" value="org.bitbucket.kienerj.moleculedatabaseframework.entity" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false" />
                <!-- if this is true it can override hibernate.hbm2ddl.auto settings -->
                <property name="generateDdl" value="false" />
                <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
            </bean>
        </property>
        <!-- put any ORM specific stuff here -->
        <property name="jpaProperties">
            <props>
                <!-- for test config only --> 
                <prop key="hibernate.hbm2ddl.auto">create-drop</prop> <
            </props>
        </property>
    </bean>
       
    
    <!-- Spring bean configuration. Tell Spring to bounce off BoneCP -->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource">
            <ref local="mainDataSource" />
        </property>
    </bean>
            
    <!-- BoneCP configuration -->
    <bean id="mainDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
        <property name="driverClass" value="org.postgresql.Driver" />
        <property name="jdbcUrl" value="jdbc:postgresql:MolDB" />
        <property name="username" value="postgres"/>
        <property name="password" value="123456"/>
        <property name="idleConnectionTestPeriod" value="60"/>
        <property name="idleMaxAge" value="240"/>      
        <property name="maxConnectionsPerPartition" value="60"/>
        <property name="minConnectionsPerPartition" value="20"/>
        <property name="partitionCount" value="3"/>
        <property name="acquireIncrement" value="10"/>                              
        <property name="statementsCacheSize" value="50"/>
        <property name="releaseHelperThreads" value="3"/>
    </bean>
    
    <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="txManager" />   
    
    
    <bean id="chemicalCompoundService" 
          class="org.bitbucket.kienerj.moleculedatabaseframework.service.ChemicalCompoundServiceImpl"/>
    
    <bean id="chemicalStructureService" autowire="autodetect"
          class="org.bitbucket.kienerj.moleculedatabaseframework.service.ChemicalStructureServiceImpl"/>
    
    <bean id="abstractJpaDAO" abstract="true"
          class="org.bitbucket.kienerj.moleculedatabaseframework.dao.AbstractJpaDAO"/>
    
    <bean id="chemicalStructureDAO" parent="abstractJpaDAO" autowire="autodetect"
          class="org.bitbucket.kienerj.moleculedatabaseframework.dao.ChemicalStructureDAO"/>
    <bean id="chemicalCompoundDAO" parent="abstractJpaDAO" autowire="autodetect"
          class="org.bitbucket.kienerj.moleculedatabaseframework.dao.ChemicalCompoundDAO"/>
</beans>

Quering

Since last post I have created some methods in the DAOs and created a Service Layer. Anyway I had to change my Queries too. The most notable change is that in queries that return 1 result only you need to change uniqueResult() to getSingleResult() but much more important is, that getSingleResult() will throw a NoResultException in case no result was found. In my case this is expected to happen and a common scenario. In my case I simulate behaviour of uniqueResult() by catching the exception and returning null.

public ChemicalStructure getByStructureKey(String structureKey) {

	try {
		ChemicalStructure structure = getEntityManager()
				.createQuery(
					"FROM ChemicalStructure structure "
				  + "WHERE structure.structureKey = :structureKey", ChemicalStructure.class)
				.setParameter("structureKey", structureKey)
				.getSingleResult();
		return structure;
	} catch (NoResultException nre) {
		return null;
	}        
}

Keep in mind you will need to change the application and test config! I wasted a lot of time because I changed application config and then ran tests which failed…Beginners mistake. 🙂

Hope this was helpful.

Advertisements

Written by kienerj

October 16, 2012 at 13:53

Posted in Database, Java, Programming

Tagged with , , ,

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: