Spring - 删除另一个 table 中引用的实体时无法捕获 ConstraintViolationException

Spring - Unable to catch ConstraintViolationException when deleting an entity referenced in another table

我正在尝试改进我在 spring MVC 应用程序中处理来自我的 DAO/服务层的异常的方式。我在下面使用的方法非常有效,不会导致 500 错误:

控制器代码

@RequestMapping(value = {"/delete-{id}-strengthUnit"}, method = RequestMethod.GET)
public String deleteStrengthUnit(@PathVariable String id) 
{
    StrengthUnit strengthUnit = strengthUnitService.findById(Integer.parseInt(id));

    try
    {
        strengthUnitService.delete(strengthUnit);

        session.setAttribute("successMessage", "Successfully deleted strength unit \"" + strengthUnit.getName() + "\"!");
    }
    catch (Exception ex)
    {
        session.setAttribute("errorMessage", "Strength unit \"" + strengthUnit.getName() + "\" is in use and cannot be deleted!");
    }

    return "redirect:/strengthUnits/list";
}

服务代码

@Override
public void delete(StrengthUnit strengthUnit) throws ConstraintViolationException
{
    dao.delete(strengthUnit);
}

DAO代码

@Override
public void delete(StrengthUnit strengthUnit) throws ConstraintViolationException
{
    super.delete(strengthUnit);
}

掌握 DAO 代码(所有 DAO 都扩展了这个抽象 DAO class)

public void delete(T entity)
{
    getSession().delete(entity);
}

此代码链正确拦截了 ConstraintViolationException(由 DAO 层抛出并由服务层转发给控制器)​​并在会话对象中设置一个变量,并在下一页中向用户显示一条漂亮的消息。所有这一切都像现在一样完美。

我正在尝试改进我的服务层,以便它可以执行所有异常捕获和日志记录任务,这些任务是与 DAO 层交互产生的。我正在尝试进行这个非常简单的更改,但我终其一生都无法弄清楚为什么在执行以下操作时无法捕获 ConstraintViolationException :

控制器代码

@RequestMapping(value = {"/delete-{id}-strengthUnit"}, method = RequestMethod.GET)
public String deleteStrengthUnit(@PathVariable String id) 
{
    StrengthUnit strengthUnit = strengthUnitService.findById(Integer.parseInt(id));

    try
    {
        strengthUnitService.delete(strengthUnit);

        session.setAttribute("successMessage", "Successfully deleted strength unit \"" + strengthUnit.getName() + "\"!");
    }
    catch (EntityInUseException ex)
    {
        session.setAttribute("errorMessage", "Strength unit \"" + strengthUnit.getName() + "\" is in use and cannot be deleted!");
    }

    return "redirect:/strengthUnits/list";
}

服务代码

@Override
public void delete(StrengthUnit strengthUnit) throws EntityInUseException
{
    try
    {
        dao.delete(strengthUnit);

        logEntryService.logDebug(LogEntry.SHARED, LogEntry.DATABASE, "Successfully deleted strength unit \"" + strengthUnit.getName() + "\"", logger);
    }
    catch (Exception ex)
    {
        logEntryService.logError(LogEntry.SHARED, LogEntry.DATABASE, "Error deleting strength unit \"" + strengthUnit.getName() + "\" : " + ex.getMessage(), logger);

        throw new EntityInUseException("Strength unit \"" + strengthUnit.getName() + "\" is in use and cannot be deleted!");
    }
}

DAO代码完全没变。通过这些简单的更改,控制器应该会收到我的自定义异常,但实际上并没有。执行不会进入服务层中的 catch 块,即使在更改之前一切正常,DAO 层没有更改也是如此。这是我在代码完全跳过 catch 块时遇到的异常:

java.sql.BatchUpdateException: The DELETE statement conflicted with the REFERENCE constraint "FKlylli8qlwprfk1t59bx6krbk3". The conflict occurred in database "MDHIS", table "dbo.tblMed", column 'id_strenghUnit'.
at net.sourceforge.jtds.jdbc.JtdsStatement.executeBatch(JtdsStatement.java:1069)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:2544)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:118)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.doExecuteBatch(BatchingBatch.java:104)
at org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl.execute(AbstractBatchImpl.java:147)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.executeBatch(JdbcCoordinatorImpl.java:212)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:633)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3278)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2474)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access0(JdbcResourceLocalTransactionCoordinatorImpl.java:39)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:98)
at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:609)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:532)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy107.delete(Unknown Source)
at com.mdenis.mdhis_webclient.controller.StrengthUnitController.deleteStrengthUnit(StrengthUnitController.java:155)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.mdenis.mdhis_webclient.filter.PermissionsFilter.doFilter(PermissionsFilter.java:125)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.mdenis.mdhis_webclient.filter.AuthenticationFilter.doFilter(AuthenticationFilter.java:75)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:417)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

我查看了另一个实体的删除操作(我尚未更改的操作),异常实际上是 DataIntegrityViolationException。即使将我的 DAO 设置为抛出该异常,我也无法让我的服务层捕获它。永远不会到达 catch 块,它会在日志中打印一条成功消息,即使我在之后立即收到 500 错误和上面的堆栈跟踪。

这对任何人都有意义吗?

谢谢!

答案在问题中。阅读堆栈跟踪。我不是引发异常的 delete() 方法。因为 delete() 只是在持久性上下文中标记要删除的实体。实际删除发生在刷新时间,当 Hibernate 实际执行删除查询时,就在提交之前。由于该服务是事务性的,因此提交发生在包装实际服务实例的事务性代理中:

同花顺:

at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3278)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2474)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access0(JdbcResourceLocalTransactionCoordinatorImpl.java:39)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271)

在 Hibernate 提交其事务时调用:

at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:98)
at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:609)

因为 Spring TransactionManager 委托给 Hibernate 提交

at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:532)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)

在服务周围的代理内部

at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy107.delete(Unknown Source)