如何从EclipseLink中捕获约束违规异常?

时间:2011-09-12 00:03:54

标签: jpa jpa-2.0 eclipselink

我在我的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不可移植,但我需要从某个地方开始。异常永远不会被抓住。有什么建议吗?

6 个答案:

答案 0 :(得分:8)

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

事实证明,在这种情况下使用EclipseLink,违反SQL约束时可以捕获的异常是 em.com发送() RollBackException 强>打电话。所以我修改了这样的持久化方法:

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字段时使用,但出于用户应用程序的目的无所谓。

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

答案 1 :(得分:4)

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

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

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

创建非参数构造函数和所需方法handleException(...)。

在此方法中,您可以捕获异常!

答案 2 :(得分:3)

EclipseLink应该只抛出PersitenceException或RollbackException,具体取决于环境以及您在EntityManager上调用的操作顺序。 你的日志记录水平是多少?您可能会看到EclipseLink记录这些异常,但仅作为RollbackException的原因而抛出。

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

答案 3 :(得分:1)

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

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

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


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

答案 4 :(得分:1)

我用它。

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类中很常见。

答案 5 :(得分:1)

2019-12-18

EclipseLink服务器上运行的Maven多模块Web应用程序中,使用{{1} },我将在此处发布我的解决方案,希望为某人节省几个小时。

Weblogic 12c中,我们有:

JTA

REST资源类标记有persistence.xml,这意味着事务从资源类的相关方法接收到请求时开始,并在该方法返回时结束。 > JTA用于管理交易。

现在,{strong}之后< property name="eclipselink.persistence-context.flush-mode" value="commit" /> 发生在资源类的方法返回(带有对REST客户端的响应)之后。

随后的意思是:

  

即使您具有非常合适的设置来捕获异常,您仍然   不能,因为像SQLIntegrityConstraintViolationException这样的异常   仅在您的INSERT / UPDATE / DELETE查询之后发生
  -一直坐在您的JPA提供者缓存中-,
  现在终于发送到数据库了。

发生的情况是在资源类的方法返回之后,并且到那时,所有异常都已被跳过< / strong>。

由于未发送查询==当时未发生异常 当执行通过@Transactional行时,您无法执行抓住
但最后,您将在服务器日志中看到异常。

解决方案:
我必须手动在JTA commit time上调用try{...}catch(Exception e){...}来强制刷新,并在适当的时间和行(通常在flush()中发生异常)才能捕获,处理,并允许我的REST方法返回预期的响应。

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

EntityManager

相关的伪代码:

try block