【问题标题】:Struts 2 + Hibernate: can't catch ConstraintViolationExceptionStruts 2 + Hibernate:无法捕获 ConstraintViolationException
【发布时间】:2010-11-24 13:18:42
【问题描述】:

我正在使用 Struts 2.2.1 和 Hibernate3 编写 Web 应用程序。当我尝试删除与其他实体具有一对多关系的实体时遇到问题。我想实现这样的场景:1)打开会话-> 2)尝试删除实体 3)提交 4)捕获 ConstraintViolationException,然后回滚并向用户显示另一个表中有相关​​项目的消息。但我无法捕捉到 ConstraintViolationException!这是我的休眠配置:

<?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";
}

以及mgr的提交代码:

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 时,commit 方法中的 catch(Exception ) 应该拦截异常,进行回滚并启动另一个事务,然后再次抛出异常。然后方法 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

请帮助我理解这种奇怪的行为(在我看来)。

谢谢!

【问题讨论】:

    标签: hibernate struts2


    【解决方案1】:

    里面有很多代码;我建议执行以下操作之一:

    • cascade delete,如果您的设计允许您这样做
    • 在尝试删除之前,使用 select 检查是否存在子项;然后提醒用户不要执行删除

    顺便说一句,使用像“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;
        }
    

    【讨论】:

    • 感谢您的回复。级联删除不适合我。对于第二点 - 我认为这是可能的变体,但我试图找出为什么我不能以这种方式处理异常。
    • 感谢更新,但没有帮助。 Web过滤器和tomcat刚刚捕获的那个异常显示堆栈跟踪:(
    • @Vitaliy:据我所见,您已经采用并修改了this article 中的代码。如果您不加修改地使用此代码会发生什么?
    • 没有结果,无论如何都失败了。我在日志中看到 org.hibernate.event.def.AbstractFlushingEventListener performExecutions,这可能与问题有关。有人知道这是什么吗?
    【解决方案2】:

    当我在 EntitesDAO 类中提交后写 HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction(); 时解决

    【讨论】:

      猜你喜欢
      • 2018-03-10
      • 2013-03-13
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-12
      相关资源
      最近更新 更多