【问题标题】:How do I catch the constraint violation exception from EclipseLink?如何从 EclipseLink 捕获约束冲突异常?
【发布时间】:2011-11-14 23:31:10
【问题描述】:

我在我的 Web 应用程序中使用 EclipseLink,我很难优雅地捕捉和处理它生成的异常。我从this thread 看到似乎是类似的问题,但我不知道如何解决或修复它。

我的代码如下所示:

public void persist(Category category) {
    try {
        utx.begin();
        em.persist(category);
        utx.commit();
    } catch (RollbackException ex) {
           // Log something
    } catch (HeuristicMixedException ex) {
           // Log something
    } catch (HeuristicRollbackException ex) {
           // Log something
    } catch (SecurityException ex) {
           // Log something
    } catch (IllegalStateException ex) {
           // Log something
    } catch (NotSupportedException ex) {
           // Log something
    } catch (SystemException ex) {
           // Log something
    }
}

当使用违反唯一性约束的实体调用 persist() 时,我会遇到大量异常,这些异常会被容器捕获并记录下来。

Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504):
  org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement
  was aborted because it would have caused a duplicate key value in a unique or 
  primary key constraint or unique index identified by 'SQL110911125638570' 
   defined on 'CATEGORY'.
 Error Code: -1
 (etc)

我尝试了以下方法:

    try {
        cc.persist(newCategory);        
    } catch (PersistenceException eee) {
        // Never gets here
        System.out.println("MaintCategory.doNewCateogry(): caught: " + eee);
    } catch (DatabaseException dbe) {
        // Never gets here neither
        System.out.println("MaintCategory.doNewCateogry(): caught: " + dbe);            
    }

我意识到使用 DataBaseException 是不可移植的,但我需要从某个地方开始。异常永远不会被捕获。有什么建议吗?

【问题讨论】:

  • “DataBaseException 不可移植”是什么意思?谢谢!
  • @WeishiZeng DataBaseException 类是由 org.eclipse 定义的,而不是 JPA 规范。因此,这可能无法与另一个 JPA 提供程序一起工作(甚至编译)。

标签: jpa jpa-2.0 eclipselink


【解决方案1】:

看来我不会再有任何关于这个问题的活动了,所以我将发布我的解决方法并留在那里。许多网络搜索都没有找到任何有用的东西。我原以为这是一个教科书案例,但我找到的教程都没有涵盖它。

在 EclipseLink 的这种情况下,您可以在违反 SQL 约束时捕获的异常是 RollBackException,它是 em.commit() 的结果强>调用。所以我修改了我的持久化方法:

public void persist(Category category) throws EntityExistsException {
    try {
        utx.begin();
        em.persist(category);
        utx.commit();
    } catch (RollbackException ex) {
        Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
        throw new EntityExistsException(ex);
    } catch (HeuristicMixedException ex) {
        Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
    } catch (HeuristicRollbackException ex) {
        Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SecurityException ex) {
        Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalStateException ex) {
        Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NotSupportedException ex) {
        Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SystemException ex) {
        Logger.getLogger(CategoryControl.class.getName()).log(Level.SEVERE, null, ex);
    }
}

所以调用者捕捉到 EntityExistsException 并采取适当的行动。日志仍然充满内部异常,但可以稍后关闭。

我意识到这有点滥用 EntityExistsException 的意图,通常仅在重新使用实体 ID 字段时使用,但出于用户应用程序的目的没关系。

如果有人有更好的方法,请发布新的答案或评论。

【讨论】:

  • 我也面临这个问题。我想显示 sql 约束异常(例如名称太长、id 存在等)。而不是检查我的代码中的约束。 (检查唯一 ID 会很昂贵。)
【解决方案2】:

编辑您的 persistence.xml 添加以下属性:

property name="eclipselink.exception-handler" value="your.own.package.path.YourOwnExceptionHandler"

现在创建类 YourOwnExceptionHandler(在正确的包上)。它需要实现 org.eclipse.persistence.exceptions.ExceptionHandler。

创建一个无参数构造函数和所需的方法handleException(...)。

在这个方法中,你可以捕获异常!

【讨论】:

  • 哇,对于一个 6 年前的问题,这是一个非常惊人的答案。这似乎不是常识。当我再次打开该项目时,我会尝试一下。
  • 艾伦,我能够捕捉到数据库异常和错误代码。但是,我仍在尝试向我的 jsf 页面发送自定义消息。
【解决方案3】:

EclipseLink 应该只抛出 PersitenceException 或 RollbackException,具体取决于环境和您在 EntityManager 上调用的操作顺序。 您的日志记录级别是多少?您很可能看到 EclipseLink 记录了这些异常,但只是作为 RollbackException 的原因抛出。

您可以使用 PU 属性关闭异常记录,但出于诊断目的,通常最好允许 EclipseLink 记录异常。

【讨论】:

  • 正如您所说,我想在开发模式下保持登录状态,以便在发生系统错误时捕获它们。但真正的问题是,当我调用 commit() 方法时,我似乎无法捕捉到异常,因此我可以在屏幕上抛出一条消息。
【解决方案4】:

我正在使用 Spring Boot 1.1.9 + EclipseLink 2.5.2。这是我可以捕获 ConstraintViolationException 的唯一方法。请注意,我的handleError(ConstraintViolationException) 是一个非常简单的实现,它只返回它发现的第一个违规。

请注意,当我切换到 Hibernate 4.3.7 和 Hibernate Validator 5.1.3 时,也需要此代码。

似乎在我的持久化JavaConfig类中添加PersistenceExceptionTranslationPostProcessor exceptionTranslation()也没有效果。


import javax.persistence.RollbackException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
class GlobalExceptionHandler
{
    @ExceptionHandler(TransactionSystemException.class)
    public ResponseEntity<Object> handleError(final TransactionSystemException tse)
    {
        if(tse.getCause() != null && tse.getCause() instanceof RollbackException)
        {
            final RollbackException re = (RollbackException) tse.getCause();

            if(re.getCause() != null && re.getCause() instanceof ConstraintViolationException)
            {
                return handleError((ConstraintViolationException) re.getCause());
            }
        }

        throw tse;
    }


    @ExceptionHandler(ConstraintViolationException.class)
    @SuppressWarnings("unused")
    public ResponseEntity<Object> handleError(final ConstraintViolationException cve)
    {
        for(final ConstraintViolation<?> v : cve.getConstraintViolations())
        {
            return new ResponseEntity<Object>(new Object()
            {
                public String getErrorCode()
                {
                    return "VALIDATION_ERROR";
                }


                public String getMessage()
                {
                    return v.getMessage();
                }
            }, HttpStatus.BAD_REQUEST);
        }

        throw cve;
    }
}

【讨论】:

    【解决方案5】:

    我用这个。

    if (!ejbGuardia.findByPkCompuestaSiExiste(bean.getSipreTmpGuardiaPK())) {
        ejbGuardia.persist(bean);
        showMessage(ConstantesUtil.MENSAJE_RESPUESTA_CORRECTA, SEVERITY_INFO);
    } else {
        showMessage("Excel : El registro ya existe. (" + bean.toString() + ")  ", SEVERITY_ERROR);
    }
    

    和我上面的函数:

    public boolean findByPkCompuestaSiExiste(Object clasePkHija) throws ClassNotFoundException {
        if (null != em.find(this.clazz, clasePkHija)) {
            return true;
        }
        return false;
    }
    

    因此我不需要为每个 Persist 编写验证程序,这在我的 DAO 类中很常见。

    【讨论】:

    • 无论你在做什么,不要用西班牙语编写代码:-D(总是英文..)
    【解决方案6】:

    2019-12-18

    作为一个很好看的问题,我刚刚遇到了与EclipseLink 非常相似的问题,在Weblogic 12c 服务器上运行并使用JTAMaven 多模块Web 应用程序中,我将发布我的解决方案在这里,希望为某人节省几个小时。

    persistence.xml 我们有:

    < property name="eclipselink.persistence-context.flush-mode"
        value="commit" />
    

    REST资源类标有@Transactional,表示事务从资源类的相关方法收到请求时开始,到该方法返回时结束。
    JTA 用于管理事务。

    现在,JTA commit time 恰好发生在资源类的方法返回之后(带有对 REST 客户端的响应)。

    这随后意味着:

    即使你有一个非常正确的设置来捕捉异常,你 不能,因为像 SQLIntegrityConstraintViolationException 这样的异常 仅在您的 INSERT/UPDATE/DELETE 查询之后发生
    -- 一直在您的 JPA 提供程序缓存中 --,
    现在终于发送到数据库了。

    会发生什么就在资源类的方法返回之后,此时,所有的异常都已经被跳过了。

    由于没有发送查询 == 没有发生异常 当时 当执行通过try{...}catch(Exception e){...} 行时,您无法捕获它,
    但最后,您会在服务器日志中看到异常。

    解决方案:
    我不得不在EntityManager 上手动调用flush() 来强制刷新,并在适当的时间和线路(基本上在try block 中)发生异常,以便能够捕获它、处理它并允许我的REST 方法带着我想要的回应返回。

    日志中最终捕获的异常(我已经屏蔽了一些不相关的信息):

    javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - x.x.x.v00000000-0000000): org.eclipse.persistence.exceptions.DatabaseException
    Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (XXXXX.UNIQUE_KEY_NAME) violated
    

    相关伪代码:

        try {
                repository.update(entity);
                repository.getEntityManager().flush();
            } catch (Exception e ) {
                log.info(e.toString());  
                ...
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-02
      • 1970-01-01
      • 2017-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多