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"/>
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“