Reputation: 4345
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 @PersistenceContext
ed.
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
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
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