Struts 2 + Hibernate: не удается поймать исключение ConstraintViolationException - PullRequest
1 голос
/ 24 ноября 2010

Я пишу веб-приложение для Struts 2.2.1 и Hibernate3. У меня проблема, когда я пытаюсь удалить сущность, которая имеет отношение один ко многим с другой сущностью. Я хочу реализовать такой сценарий: 1) Открыть сессию-> 2) Попробуйте удалить сущность 3) Подтвердить 4) Перехватить ConstraintViolationException, затем выполнить откат и показать пользователю сообщение о наличии связанных элементов в другой таблице. Но я не могу поймать ConstraintViolationException! Вот мой конфиг hibernate:

<?xml version="1.0" encoding="UTF-8"?>

"-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <property name="hibernate.dialect">org.hibernate.dialect.DerbyDialect</property>
    <property name="hibernate.connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</property>
    <property name="hibernate.connection.url">jdbc:derby:c:\soft\db\simple.db;create=true</property>
    <property name="hibernate.connection.username">app</property>
    <property name="hibernate.connection.password">app</property>
    <property name="current_session_context_class">thread</property>
    <property name="cache.provider_class">
        org.hibernate.cache.NoCacheProvider
    </property>
    <property name="show_sql">true</property>
    <property name="hbm2ddl.auto">update</property>
    <property name="hibernate.connection.pool_size">1</property>
    <mapping resource="bytes/ewt/model/Shortcuts.hbm.xml"/>
    <mapping resource="bytes/ewt/model/Categories.hbm.xml"/>
  </session-factory>
</hibernate-configuration>

Отображение по категориям класса:

    <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 27 ???? 2010 22:43:18 by Hibernate Tools 3.2.1.GA -->
<hibernate-mapping>
  <class catalog="ewt" name="bytes.ewt.model.Categories" table="categories">
    <id name="id" type="java.lang.Long">
      <column name="ID"/>
      <generator class="identity"/>
    </id>
    <property name="categoryName" type="string">
      <column length="200" name="CategoryName" not-null="true" unique="true"/>
    </property>
    <set inverse="true" name="shortcutses">
      <key>
        <column name="category_id" not-null="true"/>
      </key>
      <one-to-many class="bytes.ewt.model.Shortcuts"/>
    </set>
  </class>
</hibernate-mapping>

Управление сессиями у меня в веб-фильтре:

    package bytes.ewt.web.filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.SessionFactory;
import org.hibernate.StaleObjectStateException;
import bytes.ewt.HibernateUtil;



public class HibernateSessionRequestFilter implements Filter {

    private SessionFactory sf;
    private static Log log = LogFactory.getLog(HibernateSessionRequestFilter.class);

    public void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain chain)
            throws IOException, ServletException {

        try {
            if (!sf.getCurrentSession().isOpen())
                sf.openSession();

            sf.getCurrentSession().beginTransaction();

            // Call the next filter (continue request processing)
            chain.doFilter(request, response);

            // Commit and cleanup
            if (sf.getCurrentSession().getTransaction().isActive())
                sf.getCurrentSession().getTransaction().commit();

        } catch (StaleObjectStateException staleEx) {
            log.error("This interceptor does not implement optimistic concurrency control!");
            log.error("Your application will not work until you add compensation actions!");
            // Rollback, close everything, possibly compensate for any permanent changes
            // during the conversation, and finally restart business conversation. Maybe
            // give the user of the application a chance to merge some of his work with
            // fresh data... what you do here depends on your applications design.
            throw staleEx;
        } catch (Throwable ex) {
            // Rollback only
            ex.printStackTrace();
            try {
                if (sf.getCurrentSession().getTransaction().isActive()) {
                    //log.debug("Trying to rollback database transaction after exception");
                    sf.getCurrentSession().getTransaction().rollback();
                }
            } catch (Throwable rbEx) {
                //log.error("Could not rollback transaction after exception!", rbEx);
                rbEx.printStackTrace();
            }

            // Let others handle it... maybe another interceptor for exceptions?
            throw new ServletException(ex);
        }
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        log.debug("Initializing filter...");
        log.debug("Obtaining SessionFactory from static HibernateUtil singleton");
        sf = HibernateUtil.getSessionFactory();
    }

    public void destroy() {}

}

Когда я пытаюсь удалить категорию с помощью действия Struts2 (), я добавляю system.out для отладки лишних слов:

public String delete() throws Exception{
    if (id == null || id.length() == 0){
        addActionError(getText("categories.update.id.empty"));
        return "operation_done";
    }
    CategoriesManager mgr = ApplicationSupervisor.getInstance().getCategoriesManager();
    try{
        mgr.remove(Long.parseLong(id));
        mgr.commit();
    } catch(ConstraintViolationException ex){
        System.out.println("##Action-catch");
        //addActionError(getText("categories.in.use"));
        return "operation_done";
    } catch(Exception ex){
        System.out.println("##Action-all-catch: "+ex.getMessage() + ex.getClass() );
    }

    return "operation_done";
}

И код коммитера:

public void commit() throws Exception {
        try{
            System.out.println("##Pre-commit");
            getSession().getTransaction().commit();
            System.out.println("##After-commit");
        }
        catch(Exception ex){
            /*getSession().getTransaction().rollback();
            HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
             *
             */
            System.out.println("##Catch-restart");
            restartTransaction();
            System.out.println("##Restarted-return");
            throw ex;
        }
    }

    public void restartTransaction(){
        getSession().getTransaction().rollback();
        HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
    }

Итак, когда я вызываю commit, catch (Exception) в методе commit должен перехватывать исключение, делать откат и запускать другую транзакцию, а затем снова генерировать исключение. Затем метод delete должен перехватить ConstraintViolationException, добавить ошибку действия со связанными сущностями и перенаправить на действие индекса. Но вместо этого я получаю исключение:

SEVERE: DELETE on table 'CATEGORIES' caused a violation of foreign key constraint 'FK8725ACED1BB98B18' for key (1).  The statement has been rolled back.
24-Nov-2010 14:56:19 org.hibernate.event.def.AbstractFlushingEventListener performExecutions
SEVERE: Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: could not delete: [bytes.ewt.model.Categories#1]
        at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
        at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
        at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2541)
        at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2697)
        at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:74)
        at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:234)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:146)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
        at bytes.ewt.dao.EntitiesDAO.commit(EntitiesDAO.java:34)
        at bytes.ewt.struts.CategoriesAction.delete(CategoriesAction.java:120)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:452)
        at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:291)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:254)
        at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:176)
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:263)
        at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:68)
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:133)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:207)
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:207)
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:190)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:75)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:94)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:243)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:267)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:142)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:166)
        at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:176)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:190)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:187)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248)
        at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:52)
        at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:485)
        at org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:395)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at com.opensymphony.sitemesh.webapp.SiteMeshFilter.obtainContent(SiteMeshFilter.java:129)
        at com.opensymphony.sitemesh.webapp.SiteMeshFilter.doFilter(SiteMeshFilter.java:77)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.struts2.dispatcher.ActionContextCleanUp.doFilter(ActionContextCleanUp.java:102)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at ua.org.bytes.ewt.web.filters.HibernateSessionRequestFilter.doFilter(HibernateSessionRequestFilter.java:35)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:390)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
##Catch-restart
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
##Restarted-return
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
        at java.lang.Thread.run(Thread.java:619)
Caused by: java.sql.SQLIntegrityConstraintViolationException: DELETE on table 'CATEGORIES' caused a violation of foreign key constraint 'FK8725ACED1BB98B18' for key (1).  The statement has been rolled back.
        at org.apache.derby.impl.jdbc.SQLExceptionFactory40.getSQLException(Unknown Source)
        at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source)
        at org.apache.derby.impl.jdbc.TransactionResourceImpl.wrapInSQLException(Unknown Source)
        at org.apache.derby.impl.jdbc.TransactionResourceImpl.handleException(Unknown Source)
        at org.apache.derby.impl.jdbc.EmbedConnection.handleException(Unknown Source)
        at org.apache.derby.impl.jdbc.ConnectionChild.handleException(Unknown Source)
        at org.apache.derby.impl.jdbc.EmbedStatement.executeStatement(Unknown Source)
        at org.apache.derby.impl.jdbc.EmbedPreparedStatement.executeStatement(Unknown Source)
        at org.apache.derby.impl.jdbc.EmbedPreparedStatement.executeUpdate(Unknown Source)
        at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:23)
        at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2520)
        ... 89 more
Caused by: java.sql.SQLException: DELETE on table 'CATEGORIES' caused a violation of foreign key constraint 'FK8725ACED1BB98B18' for key (1).  The statement has been rolled back.
        at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source)
        at org.apache.derby.impl.jdbc.SQLExceptionFactory40.wrapArgsForTransportAcrossDRDA(Unknown Source)
        ... 100 more
enter code here

Пожалуйста, помогите мне понять такое странное поведение (на мой взгляд).

Спасибо!

Ответы [ 2 ]

1 голос
/ 24 ноября 2010

Там происходит много кода;Я бы предложил сделать одно из следующих действий:

  • каскадное удаление , если ваш дизайн позволяет вам сделать это
  • перед попыткой удаления, используйте команду выбора дляпроверить, существуют ли дети;затем предупредить пользователя и не выполнять удаление

Кстати, использование строковых констант, таких как «operation_done» для проверки статуса, кажется мне плохой идеей.Вам лучше использовать static final string для подобных вещей.

Редактировать : Мне кажется, я вижу, в чем ваша проблема.

Следующие части:

 } catch(ConstraintViolationException ex){
        System.out.println("##Action-catch");
        //addActionError(getText("categories.in.use"));
        return "operation_done";
    } catch(Exception ex){
        System.out.println("##Action-all-catch: "+ex.getMessage() + ex.getClass() );
    }

кажется проблематичным.Когда вы ловите исключение, будь то просто Exception или ConstraintViolationException, вы должны снова выбросить его, чтобы оно было видно «снаружи» для вашего метода удаления.Итак, этот код должен быть записан как:

 } catch(ConstraintViolationException ex){
        System.out.println("##Action-catch");
        // addActionError(getText("categories.in.use"));
        // return "operation_done";
        throw ex;
    } catch(Exception ex){
        System.out.println("##Action-all-catch: "+ex.getMessage() + ex.getClass() );
        throw ex;
    }
0 голосов
/ 25 ноября 2010

Решено, когда я написал HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction(); после коммита в классе EntitesDAO

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...