How do I catch the constraint violation exception from EclipseLink?

后端 未结 6 2404
-上瘾入骨i
-上瘾入骨i 2021-02-19 06:54

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 si

相关标签:
6条回答
  • 2021-02-19 07:05

    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!

    0 讨论(0)
  • 2021-02-19 07:06

    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.

    0 讨论(0)
  • 2021-02-19 07:08

    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;
        }
    }
    
    0 讨论(0)
  • 2021-02-19 07:11

    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.

    0 讨论(0)
  • 2021-02-19 07:13

    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());  
                ...
            }
    
    0 讨论(0)
  • 2021-02-19 07:19

    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.

    0 讨论(0)
提交回复
热议问题