項目測試發生問題,方法正常結束,但是報了
Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
錯誤,問什么不能提交呢?
經過查找發現了這么一段話
I finally understood the problem: methodA() { methodB() } @Transactional(noRollbackFor = Exception.class) methodB() { ... try { methodC() } catch (...) {...} log("OK"); } @Transactional methodC() { throw new ...(); } What happens is that even though the methodB has the right annotation, the methodC does not. When the exception is thrown, the second @Transactional marks the first transaction as Rollback only anyway.
原來,在一個transactional中如果有另一transaction發生了異常,即使你捕捉了這個異常,那么Transaction也會被定義成RollbackOnly,這也正是事務管理的原則,可是我的系統哪里出異常了呢?
原來,spring jpa JpaRepository的實現方法中用ID刪除的源碼是這樣的
@Transactional public void delete(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); T entity = findOne(id); if (entity == null) { throw new EmptyResultDataAccessException(String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1); } delete(entity); }
他是這樣實現的,先查找這個ID的對象看是否存在如果不存在則直接拋出一個非檢查型異常,就不再執行delete操作。這和我平常習慣認為的不太一樣,一般我習慣刪除沒有的記錄不會報錯,執行sql也是這樣的。而我只是在外面捕捉了這個異常,所以發生的這樣的問題。