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

Java Spring-删除另一个表中引用的实体时无法捕获ConstraintViolationException,java,spring,hibernate,exception,transactional,Java,Spring,Hibernate,Exception,Transactional,我正在努力改进在SpringMVC应用程序中处理来自DAO/服务层的异常的方法。我在下面使用的方法非常有效,不会导致500错误: 控制器代码 @RequestMapping(value = {"/delete-{id}-strengthUnit"}, method = RequestMethod.GET) public String deleteStrengthUnit(@PathVariable String id) { StrengthUnit strengthUnit = str

我正在努力改进在SpringMVC应用程序中处理来自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类)

此代码链正确地截获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.access$300(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错误和上面的stacktrace

这对任何人都有意义吗


谢谢

答案在问题中。读取堆栈跟踪。我不是引发异常的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.access$300(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 delagating到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)

当发生
ConstraintViolationException
时,您是否获得任何类型的堆栈跟踪?还有,为什么您在控制器主体本身内使用try-catch进行异常处理,而没有将其委托给
异常处理程序
它是否在日志文件中打印“错误删除强度单位”?原始帖子已更新。这确实是基于堆栈跟踪的。但是,为什么我的旧方法使用非常类似的方法,我如何才能使我的新方法工作?您的旧方法工作,因为它将对事务代理的调用包含在try块中。所以它捕获这个代理抛出的异常。您可以将新方法替换为旧方法(正确的方法):如果要捕获事务抛出的错误,事务必须在try块内开始和结束,而不是在try块外。Spring抛出指定良好的异常:只捕获这些异常,而不是捕获异常。顺便说一句,这种全局异常处理不应该到处重复。Spring的ControllerAdvice将处理此类异常放在了中心位置。我正在努力逐步改进,而全局异常管理无疑是我下一步要做的事情。这是最奇怪的部分。我尝试了一些与VolumeUnit实体稍有不同的东西,它非常相似,因为它只有一个id和一个name属性。它使用与StrengthUnit完全相同的代码链样式。我的控制器过去只捕获异常,我同意这是一种糟糕的做法。我发现实际异常的类型为DataIntegrityViolationException。因此,我改变了我的刀,服务和控制器,以捕捉这种类型,它的工作?!我可以确认,将事务性注释放在DAO上而不是服务上可以解决问题,但我在很多地方看到,您通常希望在服务上使用此注释。因此,我从所有这些中得到的是,您无法捕获由方法本身内部的事务方法导致的异常,这是有意义的。我对Spring比较有经验,但是这个东西在Spring的内部工作中非常深入。我可能会做的是停止尝试创建自定义异常,而是使用Spring抛出的异常。干杯
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy107.delete(Unknown Source)