簡體   English   中英

Spring AOP在事務回滾后帶來異常

[英]Spring AOP bring exception after transaction rollback

我只是玩代碼,並試圖了解事務和回滾在Spring中的工作方式(我正在使用spring-boot和spring-dao)。 但是,在我任職期間,我面臨着無法解釋的例外情況。 所以我有一個控制器

@RestController
public class OrderController {

@Autowired
CarSvc carSvc;

@Autowired
OrderService orderSvc;

@RequestMapping(value="/administrator/order/{id}", produces = "application/json;charset=UTF-8", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
public Message closeOrder(@PathVariable Long id){
    orderSvc.closeOrder(id);
    return new Massage("test");
}

}

該控制器將此服務稱為:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class OrderSvc extends AbstractService implements OrderService {

public void closeOrder(Long id) {
    final Order order = getDAO().findOne(id);
    Boolean isUserCanCloseOrder = order.getCarWashId().equals(getCarWashIdForAdmin());
    if(isUserCanCloseOrder){
        final Iterable<Order> all = getDAO().findAll();
        for(Order o : all){
            try {
                orderRepo.closeOrder(o);
            }catch (Exception e){
                System.out.printf("error id=" + o.getId() + " , message : " + e.getMessage());
            }

        }

    }else {
        throw new AuthorizationException("User is not allowed to close order with id = " + id);
    }
}

}

並在try塊中的此服務調用以下存儲庫

@Repository
public class OrderRepo implements OrderRepository {

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void closeOrder(Order order) {
    if(order.getClose() != null){
        order.setUpdateVersion(77);
    }else{
        order.setCarWashId(1000L + order.getId());
    }
    final List<OrderedService> services = order.getServices();
    for(OrderedService s: services){
        s.setUpdateVersion(77);
        orderedServiceDAO.save(s);
    }
    orderDAO.save(order);
}

}

邏輯如下:對於存儲庫中的部分order ,我只設置了更新版本(我需要理解是db已更新),而對於其余部分,我使用無效值的setCarWashId來調用sql異常,並看到回滾工作正常(由於sql異常)外鍵約束失敗)。

最初,該應用程序可以正常運行,並遍歷所有訂單,並向我顯示服務中所有不良訂單的System.out.printf 但是,當循環結束時,應用程序失敗,但出現以下異常:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`pitstop`.`orders`, CONSTRAINT `order_FK1` FOREIGN KEY (`car_wash_id`) REFERENCES `carwash` (`id`))
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_121]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_121]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_121]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_121]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:404) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.Util.getInstance(Util.java:387) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:932) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3878) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3814) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2478) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2625) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2551) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1861) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2073) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2009) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.PreparedStatement.executeLargeUpdate(PreparedStatement.java:5094) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1994) ~[mysql-connector-java-5.1.38.jar:5.1.38]
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:62) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3281) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3183) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3525) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:159) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:485) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at com.sun.proxy.$Proxy128.closeOrder(Unknown Source) ~[na:na]
at biz.controllers.rest.administrator.OrderController.closeOrder(OrderController.java:52) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]

我理解由於FK錯誤導致的異常,但是為什么會出現呢? 我有例外的捕獲塊。 為什么在日志中僅提及我的控制器( biz.controllers.rest.administrator.OrderController.closeOrder ),卻沒有指向服務或存儲庫的鏈接?

請向我解釋,這是怎么回事。 謝謝。

我理解由於FK錯誤導致的異常,但是為什么會出現呢? 我有例外的捕獲塊。

一旦處理鏈中任何地方出現異常,spring的事務管理器就會“將事務標記為回滾”。 即使您在處理鏈的外部調用層上有一個catch塊(就像您在控制器中一樣),請求也將失敗,並且整個事務將回滾。 通過在OrderRepo的@Transaction定義中指定rollbackFor = Exception.class ,您可以要求Spring在發生任何異常時回滾整個事務 春季論壇上討論了類似的問題。

此外,基於堆棧跟蹤,如果您在org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) 761行附近查看Spring的事務處理代碼,您將能夠了解內部發生的事情。 在此處放置一個斷點,了解Spring處理事務以及引發/處理/記錄異常的條件。 這就是我經常了解Spring事務管理正在試圖做的事情。

為什么在日志中只提到我的控制器(biz.controllers.rest.administrator.OrderController.closeOrder),卻沒有鏈接到服務或存儲庫?

at com.sun.proxy.$Proxy128.closeOrder(Unknown Source) ~[na:na]

您的堆棧跟蹤中的這一行確實表示對OrderService的調用(通過代理)。 通常,您應該在代理調用程序解析並調用實際類的某個位置進一步查看堆棧中的實際類。 您確定在此處提供了完整的堆棧跟蹤信息嗎? 您刪除了一些行嗎? 日志中是否還有其他堆棧跟蹤記錄?

我不知道原因,但是由於OrderSvc類中的@Transactional(propagation = Propagation.REQUIRED)而出現了異常。 刪除此注釋后,問題消失了

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM