kmansoor
kmansoor

Reputation: 4345

Spring's exception translation mechanism not kicking in

I have a scenario where I'm deliberately trying to violate a unique constraint, expecting Spring's DataIntegrityViolationException to be thrown, however it is never the case. javax.persistence.PersistenceException is thrown with a cause of org.hibernate.exception.ConstraintViolationException but no Spring Translation Takes place.

The DAO and Service classes are correctly annotated with @Repository and @Service and the Repository @PersistenceContexted.

Service:

  @Service
  public class BookServiceImpl implements BookService {
  @Inject private BookDAO bookDao;

  @Transactional(propagation = Propagation.REQUIRED)
  public Book createBook(Book book) {
    Book bk = null;
    try {
      bk = bookDao.makePersistent(book);
    } catch (AppDAOException e) {
      throw new AppServiceException(e);
    }
    return bk;
  }
}

My Config:

<bean id="libmsEMF"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="libmsDS" />
    <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="libmsEMF" />
    <property name="dataSource" ref="libmsDS" />
</bean>

<bean id="jpaVendorAdapter"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

    <property name="database" value="SQL_SERVER" />
    <property name="showSql" value="false" />
    <property name="generateDdl" value="false" />
    <property name="databasePlatform" value="org.hibernate.dialect.SQLServer2008Dialect" />
</bean>

<bean
    class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

Exception trace:

Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
  at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
  at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
  at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
  at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1187)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:606)
  at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:289)
  at com.sun.proxy.$Proxy60.persist(Unknown Source)
  at com.hurontg.common.persistence.AbstractGenericDAOImpl.makePersistent(AbstractGenericDAOImpl.java:103)
  ... 57 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
  at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:129)
  at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
  at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)
  at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)
  at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:211)
  at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:96)
  at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:58)
  at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3032)
  at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3558)
  at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:98)
  at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:490)
  at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:195)
  at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:179)
  at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:214)
  at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:324)
  at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
  at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
  at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
  at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
  at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
  at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
  at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
  at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)
  at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
  at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
  at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
  ... 64 more
Caused by: java.sql.SQLException: Violation of UNIQUE KEY constraint 'UK_AUTHOR_TITLE'. Cannot insert duplicate key in object 'dbo.BOOK'. The duplicate key value is (Jim Corbett, The Temple Tiger).
  at net.sourceforge.jtds.jdbc.SQLDiagnostic.addDiagnostic(SQLDiagnostic.java:372)
  at net.sourceforge.jtds.jdbc.TdsCore.tdsErrorToken(TdsCore.java:2886)
  at net.sourceforge.jtds.jdbc.TdsCore.nextToken(TdsCore.java:2328)
  at net.sourceforge.jtds.jdbc.TdsCore.getMoreResults(TdsCore.java:638)
  at net.sourceforge.jtds.jdbc.JtdsStatement.processResults(JtdsStatement.java:614)
  at net.sourceforge.jtds.jdbc.JtdsStatement.executeSQL(JtdsStatement.java:573)
  at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeUpdate(JtdsPreparedStatement.java:707)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:606)
  at org.apache.tomcat.jdbc.pool.interceptor.AbstractQueryReport$StatementProxy.invoke(AbstractQueryReport.java:235)
  at com.sun.proxy.$Proxy70.executeUpdate(Unknown Source)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:606)
  at org.apache.tomcat.jdbc.pool.interceptor.AbstractQueryReport$StatementProxy.invoke(AbstractQueryReport.java:235)
  at com.sun.proxy.$Proxy70.executeUpdate(Unknown Source)
  at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208)
  ... 85 more

Upvotes: 3

Views: 2976

Answers (2)

Dave Bower
Dave Bower

Reputation: 3567

The problem is likely to be that you don't have any PersistenceExceptionTranslator beans in your context which can unwrap Hibernate's JPA Persistence exceptions to see what the cause was.

Try adding a HibernateJpaDialect bean to your application context:

In Java

@Bean
public HibernateJpaDialect hibernateJpaDialect() {
    return new HibernateJpaDialect();
}

Or in XML

<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

Hibernate's AbstractEntityManagerImpl wraps Hibernate's exceptions into JPA exceptions before Spring performs its translation. HibernateJpaDialect implements Spring's PersistenceExceptionTranslator interface and knows how to unwrap these PersistenceExceptions.

Upvotes: 3

kazbeel
kazbeel

Reputation: 1436

As I told you I have had the same problem couple of days ago. At least in my situation/confguration I have solved the problem by using/implementing the bean PersistenceExceptionTranslationPostProcesor.

It can be included in the context this way,

XML

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

JAVA

@Bean 
public HibernateExceptionTranslator hibernateExceptionTranslator() { 
    return new HibernateExceptionTranslator(); 
}

Although the documentation says the following, I have been unable to confirm that LocalSessionFactoryBean for Hibernate4 also implements this bean out of the box (I guess it's jut matter of diving into the code).

All of Spring's applicable resource factories (e.g. LocalContainerEntityManagerFactoryBean) implement the PersistenceExceptionTranslator interface out of the box. As a consequence, all that is usually needed to enable automatic exception translation is marking all affected beans (such as Repositories or DAOs) with the @Repository annotation, along with defining this post-processor as a bean in the application context.

Upvotes: 2

Related Questions