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
的一部分,我只设置了更新版本(我需要了解数据库是否已更新),其余部分 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
) 而没有 link 服务或存储库?
请解释一下,这是怎么回事。
谢谢。
I understand the exception due to incorrect FK but why it appear? I have catch block for exception.
只要处理链任何地方出现异常,spring的事务管理器"marks the transaction for rollback".即使您在处理链的外部调用层有一个 catch 块(就像您在控制器中有它一样),请求也会失败并且整个事务将被回滚。通过在 OrderRepo 的 @Transaction
定义中指定 rollbackFor = Exception.class
,您要求 Spring 在发生任何异常时回滚 整个事务 。讨论了一些类似的问题 here on spring forum。
此外,根据您的堆栈跟踪,如果您查看 org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
中第 761 行附近的 Spring 事务处理代码,您将能够了解内部发生的情况。在那里放置一个断点并了解 Spring 正在处理事务和 throwing/handling/logging 异常的条件。这就是我经常理解 Spring 事务管理试图做的事情的方式。
And why in log was mentioned only my controller (biz.controllers.rest.administrator.OrderController.closeOrder) and there is not link to services or repository?
at com.sun.proxy.$Proxy128.closeOrder(Unknown Source) ~[na:na]
堆栈跟踪中的这一行确实表示对 OrderService 的调用(通过代理)。通常,您应该在代理调用程序解析并调用实际 class 的某个点进一步看到堆栈跟踪中的实际 class。你确定你在这里提供了完整的堆栈跟踪吗?你删除了一些行吗?日志中是否还有其他堆栈跟踪或只有这一个?
我不知道原因,但由于 @Transactional(propagation = Propagation.REQUIRED)
在 class OrderSvc
中出现了异常。删除此注释后,问题消失了
我只是玩代码,并尝试了解 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
的一部分,我只设置了更新版本(我需要了解数据库是否已更新),其余部分 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
) 而没有 link 服务或存储库?
请解释一下,这是怎么回事。 谢谢。
I understand the exception due to incorrect FK but why it appear? I have catch block for exception.
只要处理链任何地方出现异常,spring的事务管理器"marks the transaction for rollback".即使您在处理链的外部调用层有一个 catch 块(就像您在控制器中有它一样),请求也会失败并且整个事务将被回滚。通过在 OrderRepo 的 @Transaction
定义中指定 rollbackFor = Exception.class
,您要求 Spring 在发生任何异常时回滚 整个事务 。讨论了一些类似的问题 here on spring forum。
此外,根据您的堆栈跟踪,如果您查看 org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
中第 761 行附近的 Spring 事务处理代码,您将能够了解内部发生的情况。在那里放置一个断点并了解 Spring 正在处理事务和 throwing/handling/logging 异常的条件。这就是我经常理解 Spring 事务管理试图做的事情的方式。
And why in log was mentioned only my controller (biz.controllers.rest.administrator.OrderController.closeOrder) and there is not link to services or repository?
at com.sun.proxy.$Proxy128.closeOrder(Unknown Source) ~[na:na]
堆栈跟踪中的这一行确实表示对 OrderService 的调用(通过代理)。通常,您应该在代理调用程序解析并调用实际 class 的某个点进一步看到堆栈跟踪中的实际 class。你确定你在这里提供了完整的堆栈跟踪吗?你删除了一些行吗?日志中是否还有其他堆栈跟踪或只有这一个?
我不知道原因,但由于 @Transactional(propagation = Propagation.REQUIRED)
在 class OrderSvc
中出现了异常。删除此注释后,问题消失了