Spring 异常转换不适用于 JPA



我的道类com.myapp.dao.hibernate.XyzDaoImpl@Repository注释

@Repository
public class UserDaoImpl extends GenericDaoJpa implements UserDao {
...
    @Override
    public void saveUser(User user) throws Exception{
        entityManager.persist(user);
    }
...
}

app-data.xml如下:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
       <property name="driverClassName" value="${db.driver}" />
        <property name="url" value="${db.url}" />
       <property name="username" value="${db.username}" />
     <property name="password" value="${db.password}" />
     <property name="initialSize" value="5" />
        <property name="maxActive" value="0" />
  </bean>

    <!-- JPA Entity Manager Factory -->
    <bean id="entityManagerFactory"  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" 
          p:dataSource-ref="dataSource" p:jpaDialect-ref="jpaDialect"
        p:jpaVendorAdapter-ref="jpaVendorAdapter"
    />
    <bean name="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">
    </bean>
    <bean name="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"></property>
    </bean>
    <!-- detect les @persistentContext in dao -->
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

     <!-- This will ensure that hibernate or jpa exceptions are automatically translated into
         Spring's generic DataAccessException hierarchy -->
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

 <bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />
    <!-- Transaction Config -->
    <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"
          p:entityManagerFactory-ref="entityManagerFactory"/>
   <context:component-scan base-package="com.myapp.dao.hibernate" />
 <tx:annotation-driven transaction-manager="txManager" />

现在,在我的jUnit测试方法中,我故意插入了一个重复的对象,我得到了javax.persistence.PersistenceException而不是弹簧异常。

@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;
}
...
}

这是完整的堆栈跟踪

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(<generated>)
    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(<generated>)
    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

编辑1:从saveUser() DAO方法中删除抛出异常后,似乎Spring异常转换正在工作。我的服务方法捕获数据完整性违规异常并正常返回。但是,测试方法不会获得返回,而是获得异常:TransactionSystemException。我怎样才能避免这种破坏一切的不需要的异常?

编辑2:Spring 异常翻译工作正常,问题来自 JPA/EntityManager。查看完整的故事以及我如何设法解决它:http://tinyurl.com/439dmul

关于你关于TransactionSystemException的后一个问题:退出方法时会抛出@Transactional异常,以表明事务已被回滚。

如果你想可靠地捕获约束冲突,你需要围绕事务而不是在事务内部进行。例如,通过在方法中使用编程事务划分saveUser()而不是@Transactional,或者通过将try-catch子句添加到顶级方法中。

最新更新