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

发布于 2024-12-03 19:35:02 字数 1883 浏览 5 评论 0原文

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

我的代码如下所示:

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 不可移植,但我需要从某个地方开始。例外永远不会被捕获。有什么建议吗?

I am using EclipseLink in my web application, and I am having a hard time gracefully catching and handling Exceptions it generates. I see from this thread what seems to be a similar problem, but I don't see how to work around or fix it.

My code looks like this:

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

When persist() is called with an entity that violates a uniqueness constraint, I get an explosion of exceptions that are caught and logged by the container.

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)

I have tried the following:

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

I realize that using DataBaseException is not portable, but I need to start somewhere. The exceptions never get caught. Any suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

橙味迷妹 2024-12-10 19:35:02

看起来我不会再对这个问题进行任何活动,因此我将发布我的解决方法并就此保留。许多网络搜索都没有找到太多有用的东西。我本以为这是教科书案例,但我找到的教程都没有涵盖它。

事实证明,在 EclipseLink 的这种情况下,违反 SQL 约束时可以捕获的异常是 RollBackException,它是 em.commit() 调用的结果。所以我修改了我的 persist 方法,如下所示:

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 字段时使用,但出于用户应用程序的目的,它并不使用事情。

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

It looks like I won't get any more activity on this question, so I will post my work-around and leave it at that. A number of web searches haven't found much of anything that is helpful. I would have thought this is a textbook case but none of the tutorials I have found covers it.

As it turns out in this condition with EclipseLink, the Exception you can catch when the SQL constraint is violated is the RollBackException that is the result of the em.commit() call. So I have modified my persist method like this:

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

So the caller catches the EntityExistsException and takes the appropriate action. The log still fills up with the internal exceptions but that can be shut off later.

I realize that this is a bit of an abuse of the intent of the EntityExistsException that is normally only used when an entity ID field is re-used, but for the purposes of the user application it doesn't matter.

If anyone has a better approach please post a new answer or comment.

笑梦风尘 2024-12-10 19:35:02

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

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

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

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

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

Edit your persistence.xml adding the following property:

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

Now create the class YourOwnExceptionHandler (on the correct package). It requires to implement org.eclipse.persistence.exceptions.ExceptionHandler.

Create a non argument constructor and the required method handleException(...).

Inside this method, you can catch the exceptions!

野心澎湃 2024-12-10 19:35:02

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

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

EclipseLink should only be throwing either a PersitenceException or a RollbackException depending on the environment and the order of operations you are calling on the EntityManager.
What is your logging level? It is likely that you are seeing these exceptions logged by EclipseLink but only thrown as causes of the RollbackException.

You can turn off exception logging with the PU property but for diagnostic purposes it is generally better to allow EclipseLink to log the exceptions.

忱杏 2024-12-10 19:35:02

2019-12-18

由于这是一个非常受关注的问题,我刚刚在 Maven 多模块网络中遇到了与 EclipseLink 非常相似的问题应用程序在 Weblogic 12c 服务器上运行并使用 JTA,我将在这里发布我的解决方案,希望能为某人节省几个小时。

persistence.xml 中,我们有:

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

REST 资源类被标记为 @Transactional,这意味着事务在相关对象收到请求时开始。资源类的方法,当该方法返回时结束。
JTA 用于管理事务。

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

这意味着:

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

发生在资源类的方法返回之后,并且此时,所有异常都已被跳过

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

解决方案:
我必须在 EntityManager 上手动调用 flush() 来强制刷新并在适当的时间和行发生异常(基本上在 try 块 >) 能够捕获它、处理它,并允许我的 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());  
            ...
        }

2019-12-18

As its a very well viewed question and I just had a very similar issue with EclipseLink, in a Maven multi module web application running on Weblogic 12c server and using JTA, I am going to post my solution here, hoping to save a couple hours for someone.

In the persistence.xml we are having:

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

The REST resource class was marked with @Transactional, meaning that the transaction starts at the point when the request has been received by the related method of the resource class, and it ends when this method returns.
JTA used for managing the transactions.

Now, JTA commit time happens to occur AFTER the resource class's method returns (with a response to the REST client).

Which subsequently means that:

Even though you had a very proper setup to catch the Exception, you
cannot, as exceptions like SQLIntegrityConstraintViolationException
occur only AFTER your INSERT/UPDATE/DELETE query
--that has been sitting all this time in your JPA provider cache--,
now finally sent to the database.

Which happens just after the resource class's method returns, and at that point, all the exceptions has been skipped already.

Since no query sent == no exception occured at the time when the execution ran through the try{...}catch(Exception e){...} lines, you were not able to catch it,
but at the end, you will see the exception in the server's log.

Solution:
I had to manually call flush() on EntityManager to force flush and the exception to occur at the proper time and line (basically in the try block) to be able to catch it, handle it, and allow my REST method to return with my intended response.

The final caught exception in the log (I have masked some not related info):

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

Related pseudo code:

    try {
            repository.update(entity);
            repository.getEntityManager().flush();
        } catch (Exception e ) {
            log.info(e.toString());  
            ...
        }
ぇ气 2024-12-10 19:35:02

我正在使用 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;
    }
}

I'm using Spring Boot 1.1.9 + EclipseLink 2.5.2. This is the only way I can catch ConstraintViolationException. Note that my handleError(ConstraintViolationException) is a very simple implementation which just returns the first violation it finds.

Note that this code was also required when I switched to Hibernate 4.3.7 and Hibernate Validator 5.1.3.

It seems that adding PersistenceExceptionTranslationPostProcessor exceptionTranslation() to my persistence JavaConfig class also has no effect.


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;
    }
}
听闻余生 2024-12-10 19:35:02

我用这个。

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

I use this.

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

and my function from above:

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

With that I dont need to program a validation for each Persist, its common in the my DAO Classes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文