Category Archives: jpa

Configuring Ehcache with JPA, Hibernate & Spring

This post details on the steps required to configure ehcache as the second level hibernate cache when jpa, hibernate and spring are used in combination

Configuring the cache

Add the below properties to the persistence.xml configuration file

<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<property name="hibernate.generate_statistics" value="true"/>

Read the rest of this entry

Advertisements

Supporting Custom Isolation Levels With JPA

This post provides a workaround for supporting custom isolation levels with JPA. If you use the HibernateJpaDialect (provided by spring)  and try to set an isolation level different from the default one you would get an exception “Standard JPA does not support custom isolation levels – use a special JpaDialect”

The above error would be seen when your configuration is as below

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" lazy-init="true">
   <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
          lazy-init="true">
        <property name="persistenceUnitName" value="spmsoftwareunit"/>
        <property name="persistenceXmlLocation" value="classpath:com/spmsoftware/sampleapp/conf/persistence.xml"/>
        <property name="dataSource" ref="oracleDataSourceFactoryBean"/>
        <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.id.new_generator_mappings">true</prop>
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
                <prop key="hibernate.jdbc.batch_size">3</prop>
                <prop key="hibernate.jdbc.batch_versioned_data">true</prop>
                <prop key="hibernate.jdbc.factory_class">com.spmsoftware.sampleapp.persistence.OracleBatchingBatcherFactory</prop>
                <prop key="hibernate.hbm2ddl.auto">none</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

and the transaction is applied as

@Transactional(isolation=Isolation.SERIALIZABLE, propagation=Propagation.REQUIRED)

WorkAround

We can extend HibernateJpaDialect to modify the connection instance before the transaction starts

import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;

public class HibernateExtendedJpaDialect extends HibernateJpaDialect {

    private Logger logger = LoggerFactory.getLogger(HibernateExtendedJpaDialect.class);

    /**
     * This method is overridden to set custom isolation levels on the connection
     * @param entityManager
     * @param definition
     * @return
     * @throws PersistenceException
     * @throws SQLException
     * @throws TransactionException
     */
    @Override
    public Object beginTransaction(final EntityManager entityManager,
            final TransactionDefinition definition) throws PersistenceException,
            SQLException, TransactionException {
        Session session = (Session) entityManager.getDelegate();
        if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
            getSession(entityManager).getTransaction().setTimeout(definition.getTimeout());
        }

        entityManager.getTransaction().begin();
        logger.debug("Transaction started");

        session.doWork(new Work() {

            public void execute(Connection connection) throws SQLException {
                logger.debug("The connection instance is {}", connection);
                logger.debug("The isolation level of the connection is {} and the isolation level set on the transaction is {}",
                        connection.getTransactionIsolation(), definition.getIsolationLevel());
                DataSourceUtils.prepareConnectionForTransaction(connection, definition);
            }
        });

        return prepareTransaction(entityManager, definition.isReadOnly(), definition.getName());
    }

}

The above implementation takes care of setting the isolation level specified on the transaction definition by calling DataSourceUtils.prepareConnectionForTransaction(connection, definition);

Since the isolation level is changed on the connection instance, resetting it is important. This would have to be done in the data source since the connection gets closed as soon as the transaction is committed. There is no way to intercept the JpaTransactionManager code flow to reset the isolation level after commit is done.
By decorating the data source and setting the isolation level before the connection is given by the connection pool, one can achieve resetting of the isolation level on the connection instance to default (which in most of the cases is read committed).

JPA Query – getSingleResult() – Not a good API

JPA Query API has two methods to retrieve results from an select query.

1. getSingleResult() – Execute a SELECT query that returns a single untyped result.

2. getResultList() – Execute a SELECT query and return the query results as an untyped List.

The weird part of the getSingleResult() method is that it throws a NoResultException – if there is no result found. NoResultException is a runtime exception.
According to the EffectiveJava “Use checked exceptions for conditions from wich the caller can reasonably be expected to recover. Use runtime exceptions to indicate programming errors”. Getting no result from an select query is not a programming error.

The workaround to overcome this api is to use getResultList() method and check if the list is empty. getSingleResult() can only be used in situations where “I am totally sure that this record exists. Shoot me if it doesn’t