Spring - невозможно перехватить ConstraintViolationException при удалении объекта, на который есть ссылка в другой таблице - PullRequest
0 голосов
/ 05 июля 2018

Я пытаюсь улучшить способ обработки исключений, исходящих из моих уровней DAO / service, в моем весеннем приложении MVC. Метод, который я использую ниже, работает очень хорошо и не вызывает ошибку 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)

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 полностью не изменился. С этими простыми изменениями контроллер должен получить мое пользовательское исключение, но это не так. Выполнение не входит в блок перехвата на уровне службы, хотя до изменений все работало идеально, без изменений на уровне 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 и вышеприведенную трассировку стека.

Имеет ли это смысл для кого-либо?

Спасибо!

1 Ответ

0 голосов
/ 05 июля 2018

Ответ в вопросе. Прочитайте трассировку стека. Я не метод delete (), который выдает исключение. Потому что delete () просто состоит в маркировке объекта для удаления в контексте постоянства. Фактическое удаление происходит во время сброса, когда Hibernate фактически выполняет запрос на удаление, непосредственно перед фиксацией. А поскольку служба является транзакционной, фиксация происходит в транзакционном прокси-сервере, обертывающем фактический экземпляр службы:

Flush:

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, выполняющий функцию 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)
...