title: Spring exception translation isn't working with JPA
body: <p>My dao class <code>com.myapp.dao.hibernate.XyzDaoImpl</code> is annotated with <code>@Repository</code></p>
  
  <pre><code>@Repository
  public class UserDaoImpl extends GenericDaoJpa implements UserDao {
  
  ...
      @Override
      public void saveUser(User user) throws Exception{
          entityManager.persist(user);
      }
  ...
  }
  </code></pre>
  
  <p>My <code>app-data.xml</code> is as follow:</p>
  
  <pre><code>&lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"&gt;
         &lt;property name="driverClassName" value="${db.driver}" /&gt;
          &lt;property name="url" value="${db.url}" /&gt;
         &lt;property name="username" value="${db.username}" /&gt;
       &lt;property name="password" value="${db.password}" /&gt;
       &lt;property name="initialSize" value="5" /&gt;
          &lt;property name="maxActive" value="0" /&gt;
    &lt;/bean&gt;
  
  
      &lt;!-- JPA Entity Manager Factory --&gt;
      &lt;bean id="entityManagerFactory"  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" 
            p:dataSource-ref="dataSource" p:jpaDialect-ref="jpaDialect"
          p:jpaVendorAdapter-ref="jpaVendorAdapter"
      /&gt;
  
      &lt;bean name="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"&gt;
      &lt;/bean&gt;
  
      &lt;bean name="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"&gt;
          &lt;property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"&gt;&lt;/property&gt;
      &lt;/bean&gt;
  
      &lt;!-- detect les @persistentContext in dao --&gt;
      &lt;bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /&gt;
  
  
       &lt;!-- This will ensure that hibernate or jpa exceptions are automatically translated into
           Spring's generic DataAccessException hierarchy --&gt;
      &lt;bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/&gt;
  
  
   &lt;bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" /&gt;
  
      &lt;!-- Transaction Config --&gt;
      &lt;bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"
            p:entityManagerFactory-ref="entityManagerFactory"/&gt;
  
     &lt;context:component-scan base-package="com.myapp.dao.hibernate" /&gt;
  
   &lt;tx:annotation-driven transaction-manager="txManager" /&gt;
  </code></pre>
  
  <p>Now, in my jUnit test method, I deliberately insert a duplicate object and I got <code>javax.persistence.PersistenceException</code> instead of a spring exception.</p>
  
  <pre><code>@Test public void testSaveUser() {
          String login = "duplicateLogin";
  
          User user = new User();
          user.setLogin(login);
          user.setPasswd("pass");
          InformationBean rep = ms.saveUser(user);
          assertEquals(Constants.REPLY_403, rep);
          System.out.println("user was not saved "+user);     
  }
  
  
  @Service
  public class MessagingService{
  ...
  
  @Transactional(propagation=Propagation.REQUIRED)
  public InformationBean saveUser(User user) {
      InformationBean info ;
      try {
          userDao.saveUser(user);
          info = Constants.REPLY_200;
      }catch(DataIntegrityViolationException ex) {
          logger.warn("login already used.");
          info = Constants.REPLY_403;
      }catch(ConstraintViolationException cve) {
          logger.warn("login already used.");
          info = Constants.REPLY_403;
      }
      catch(Exception e) {
          logger.error("Oups error",e);       
          info = Constants.REPLY_500;
      }
  
      return info;
  }
  ...
  }
  </code></pre>
  
  <p>Here is the complete stack trace</p>
  
  <pre><code>DEBUG - Creating new transaction with name [com.myapp.service.MessagingService.saveUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
  DEBUG - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl@f6852d] for JPA transaction
  DEBUG - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@79df82]
  Hibernate: insert into User (email, login, passwd) values (?, ?, ?)
  8928 [main] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
  8929 [main] ERROR org.hibernate.util.JDBCExceptionReporter - Duplicate entry 'u123456789012' for key 2
  ERROR - Oups error
  javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not insert: [com.myapp.model.User]
      at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1235)
      at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1168)
      at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1174)
      at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:674)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
      at $Proxy32.persist(Unknown Source)
      at com.myapp.dao.hibernate.UserDaoImpl.saveUser(UserDaoImpl.java:60)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
      at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
      at $Proxy34.saveUser(Unknown Source)
      at com.myapp.service.MessagingService.saveUser(MessagingService.java:237)
      at com.myapp.service.MessagingService$$FastClassByCGLIB$$48584635.invoke(&lt;generated&gt;)
      at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
      at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:688)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
      at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
      at com.myapp.service.MessagingService$$EnhancerByCGLIB$$4c0c06a9.saveUser(&lt;generated&gt;)
      at com.myapp.service.MessagingServiceTest.testSaveUser(MessagingServiceTest.java:112)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
      at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
      at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
      at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
      at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
      at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
      at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
      at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
      at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
      at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
      at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
      at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
      at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
      at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
      at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
      at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
      at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
      at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
      at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
      at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
      at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
      at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
      at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
      at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
  Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.myapp.model.User]
      at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
      at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
      at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:64)
      at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2329)
      at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2836)
      at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71)
      at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
      at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321)
      at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
      at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
      at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:69)
      at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:179)
      at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
      at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
      at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:800)
      at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:774)
      at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:778)
      at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:668)
      ... 56 more
  Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'u123456789012' for key 2
      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
      at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
      at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
      at com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
      at com.mysql.jdbc.Util.getInstance(Util.java:384)
      at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1041)
      at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3562)
      at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3494)
      at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1960)
      at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2114)
      at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2696)
      at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2105)
      at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2398)
      at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2316)
      at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2301)
      at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:101)
      at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:94)
      at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:57)
      ... 71 more
  DEBUG - Initiating transaction commit
  DEBUG - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@f6852d]
  DEBUG - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@f6852d] after transaction
  DEBUG - Closing JPA EntityManager
  </code></pre>
  
  <p><em>EDIT1: After removing the throws Exception from the saveUser() DAO method, it seems that Spring exception translation is working. My service method catchs DataIntegrityViolationException and return normally. However, the test method doesn't get the return but instead get an exception: TransactionSystemException. How can I avoid such unwanted exception that breaks everything?</em></p>
  
  <p><em>Edit2: 
  Spring exception translation was working fine, the problem comes from JPA/EntityManager.
  See the complete story and how I managed to solve it: <a href="http://tinyurl.com/439dmul" rel="nofollow">http://tinyurl.com/439dmul</a></em></p>