gipinani
gipinani

Reputation: 14418

Spring Thrown JpaSystemException instead of DuplicateKeyException

I'm in troubles with DataAccessException handling.

When an uniquess key constraint is violated I got an JpaSystemException, and not a DuplicateKeyException!

I found some thread talking about this problem, but no one help me to solve the problem. How can I map to a concrete org.springframework.dao exception?

My persistence.xml file look like this:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>        
        <class>...</class>
        <class>...</class>
        ...
        <exclude-unlisted-classes />
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/> 
            <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
            <property name="hibernate.connection.charSet" value="UTF-8"/>
            <property name="hibernate.show.sql" value="true"/>

            <property name="hibernate.ejb.event.post-insert" value="org.hibernate.ejb.event.EJB3PostInsertEventListener,org.hibernate.envers.event.AuditEventListener" />
        <property name="hibernate.ejb.event.post-update" value="org.hibernate.ejb.event.EJB3PostUpdateEventListener,org.hibernate.envers.event.AuditEventListener" />
        <property name="hibernate.ejb.event.post-delete" value="org.hibernate.ejb.event.EJB3PostDeleteEventListener,org.hibernate.envers.event.AuditEventListener" />
        <property name="hibernate.ejb.event.pre-collection-update" value="org.hibernate.envers.event.AuditEventListener" />
            <property name="hibernate.ejb.event.pre-collection-remove" value="org.hibernate.envers.event.AuditEventListener" />
        <property name="hibernate.ejb.event.post-collection-recreate" value="org.hibernate.envers.event.AuditEventListener" />
    </properties>         
    </persistence-unit>    
    <persistence-unit name="persistenceUnitI24" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>...</class>
        <class>...</class>
        <exclude-unlisted-classes />
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="validate"/>
            <property name="hibernate.connection.charSet" value="UTF-8"/>
            <property name="hibernate.showsql" value="true"/>           
        </properties>
    </persistence-unit> 
</persistence>

And the configurations about mysql transaction manager:

<bean id="entityManagerFactory" 
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
          p:dataSource-ref="dataSource"
          p:persistenceUnitName="persistenceUnit"
          p:jpaProperties="">
          <property name="jpaVendorAdapter">
        <bean
          class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
              <property name="showSql" value="true" />
              <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
              <property name="database" value="MYSQL"/>           
         </bean>
       </property>
           <property name="jpaDialect">
         <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
           </property>
    </bean>

    <bean id="transactionManager"
          class="org.springframework.orm.jpa.JpaTransactionManager"
          p:entityManagerFactory-ref="entityManagerFactory"/>

    <tx:annotation-driven transaction-manager="transactionManager" />

I've omitted the configuration about sqlserver because I think that is not relevant. I'm not interested in translation error about this db.

I've also included the bean:

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

Trying to to do the following simple junit test:

@Test(expected=DuplicateKeyException.class)
public void createNewUbi(){
    Ubi ubi = new Ubi();
    ubi.setCode("A");
    ubiDao.save(ubi);       

    ubi = new Ubi();
    ubi.setCode("A");

    ubiDao.save(ubi);                   
}

I got from my log:

ERROR: org.hibernate.util.JDBCExceptionReporter - Duplicate entry 'A' for key 'code'

Catching the exception of the second save call I noticed that I have:

JpaSystemException: cause: javax.persistence.PersistenceException
PersistenceException: cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException 

If I try to do an insertion from the webapp I got this Exception:

org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:311)
    org.springframework.orm.jpa.aspectj.JpaExceptionTranslatorAspect.ajc$afterThrowing$org_springframework_orm_jpa_aspectj_JpaExceptionTranslatorAspect$1$18a1ac9(JpaExceptionTranslatorAspect.aj:15)
    it.cpmapave.pm.dao.jpa.GenericDaoJpa.save(GenericDaoJpa.java:29)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    java.lang.reflect.Method.invoke(Method.java:597)
    org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
    org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    $Proxy56.save(Unknown Source)
    it.cpmapave.pm.service.amministrazione.SettoreServiceImpl.saveSettore(SettoreServiceImpl.java:34)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    java.lang.reflect.Method.invoke(Method.java:597)
    org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
    org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    $Proxy57.saveSettore(Unknown Source)
    it.cpmapave.pm.controller.amministrazione.SettoreController.create(SettoreController.java:42)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    java.lang.reflect.Method.invoke(Method.java:597)
    org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:436)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:669)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:585)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:113)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:369)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:177)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:187)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:168)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
    org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
    org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    com.springsource.insight.collection.tcserver.request.HttpRequestOperationCollectionValve.invoke(HttpRequestOperationCollectionValve.java:84)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
    org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:279)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:300)
    java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    java.lang.Thread.run(Thread.java:662)

I've enabled mysql query logging and I got the following query:

insert into ubi (code, version) values ('A', 0)

If I try to execute the query from mysql console I got: ERROR 1062 (23000): Duplicate entry 'A' for key 'code'

Searching in sql-error-codes.xml in package org.springframework.jdbc.support of spring-jdbc-3.0.6.RELEASE.jar i found that for mysql db the error code 1062 is configured in this way:

<property name="duplicateKeyCodes">
    <value>1062</value>
</property>

So I think that there is some Exception translating problem due to a misconfigured properties.. but which?

I'm using Spring 3.0.6.RELEASE and Hibernate 3.6.9.FINAL

Thank you for any reply! Marco

Upvotes: 8

Views: 12601

Answers (4)

Ankur Pathak
Ankur Pathak

Reputation: 1

Use Spring ORM JPA Extension:

Extension to Spring ORM JPA module to provide Standard Spring SQL Exception Translation similar to Spring JDBC module.

Home Page of extension is: https://github.com/ankurpathak/spring-orm-jpa-extension

Simple example to use the extension in your project is:

@Configuration
public class JpaConfig {

    private final  JdbcTemplate jdbcTemplate;

    public JpaConfig(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }


    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(JdbcTemplate jdbcTemplate){
        LocalContainerEntityManagerFactoryBean emf = new ExtendedLocalContainerEntityManagerFactoryBean(jdbcTemplate.getExceptionTranslator());
        emf.setDataSource(jdbcTemplate.getDataSource());
        emf.setPackagesToScan("com.github.ankurpathak.hibernatedemo");
        emf.setJpaVendorAdapter(new ExtendedHibernateJpaVendorAdaptor(jdbcTemplate.getExceptionTranslator()));
        Properties jpaProperties =  new Properties();
        jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        jpaProperties.setProperty("hibernate.show_sql", String.valueOf(Boolean.TRUE));
        jpaProperties.setProperty("hibernate.format_sql", String.valueOf(Boolean.TRUE));
        jpaProperties.setProperty("hibernate.hbm2ddl.auto", "validate");
        emf.setJpaProperties(jpaProperties);
        return emf;
    }
}

Upvotes: 0

nilsi
nilsi

Reputation: 10761

I had this set in my config.

transactionManager.setJpaDialect(new HibernateJpaDialect());

I commented it away and suddenly my application started to give DuplicateKeyException

Upvotes: 1

Ryan Stewart
Ryan Stewart

Reputation: 128939

This problem isn't related to Spring or to your database driver. I had to work through this myself a while back. I was surprised to find that the hibernate-entitymanager jar, which lets you use Hibernate as a JPA provider, has some poor exception translation in it. The AbstractEntityManagerImpl.convert() method is responsible for converting HibernateExceptions into JPA PersistenceExceptions, but it doesn't handle any kind of "duplicate key" or "unique constraint" exceptions at all. Even the latest code on Github doesn't seem to have fixed it. If you take a look at it, the convert() method has a long chain of ifs, but none of them cover that particular kind of exception, so it falls through to the bottom and gets translated to a generic PersistenceException. It's rather annoying.

Upvotes: 4

Mark G.
Mark G.

Reputation: 3280

Make sure your EntityManager has a vendorAdapter set on it. This will allow your jpa provider to provide advanced error codes.

Upvotes: 4

Related Questions