简体   繁体   中英

How to handle JPA unique constraint violations?

When a unique constraint is violated, a javax.persistence.RollbackException is thrown. But there could be multiple reasons to throw a RollbackException . How can I find out that a unique constraint was violated?

try {
    repository.save(article);
}
catch(javax.persistence.RollbackException e) {
    // how to find out the reason for the rollback exception?
}

How can I find out that a unique constraint was violated?

Exception are chained, you have to call getCause() recursively to get the provider specific exception (and maybe go down to the SQLException ) to translate it into something your application can handle nicely for your user. The following will print the chain of exception:

for (t = e.getCause(); t != null; t = t.getCause()) {
    logger.debug("Exception:" + t);
}

For the exception handling and the "translation", you could do something like what Spring does (see the various JpaDialect classes, eg HibernateJpaDialect to get an idea).

All this is not nice, this code won't be portable and finding what attribute(s) caused the violation won't be easy. This somehow confirms that there is no elegant and portable way to handle constraint violations in JPA .

使用e.getCause()检查导致回滚的原因。

You can use the following :

If you are using spring framework then you can use :

org.springframework.dao.DataIntegrityViolationException

If standard JPA following can be used

org.hibernate.exception.ConstraintViolationException

At sql level following can be used :

java.sql.SQLIntegrityConstraintViolationException

compiler returns the exception SQLIntegrityConstraintViolationException , while trying to violate unique constraint.

Use the following catch block concept, to handle proper exceptions.

catch(SQLIntegrityConstraintViolationException e) 
{
  // Error message for integrity constraint violation
}
catch(Exception e)
{
 // Other error messages
}

This might very late for you but here's how I solved it for PostGres.

catch (DataIntegrityViolationException e) {

            for (Throwable t = e.getCause(); t != null; t = t.getCause()) {

                if (PSQLException.class.equals(t.getClass())) {
                    PSQLException postgresException = (PSQLException) t;

                    // In Postgres SQLState 23505=unique_violation
                    if ("23505".equals(postgresException.getSQLState())) {
                        throw new CustomDataAlreadyExistsException("YourErrorCode", e);
                    }
                }
            }
            throw new SomeOtherException("YourErrorCode2", e);

        }

i think printing the stack trace will help you to know this. e.printStackTrace();

You can do like this :

StringWriter writer=new StringWriter(); //remains the message from stack trace.
e.printStackTrace(new PrintWriter(writer));
String message=writer.toString(); // gets the message of full stack trace.

And then view the information of exception.

You have to catch javax.persistence.PersistenceException. Thrown exception is org.hibernate.exception.ConstraintViolationException.

Exception Message : org.hibernate.exception.ConstraintViolationException: Duplicate entry '893073116V-1234567' for key 'nic_account_no'

The easiest way to pin point the cause of constraint violation, is to define the name of the constraint properly in the first place:

Defining Unique Constraint:

Using @UniqueConstraint inside @Table , we can either define one column or multiple columns constraints, along with a custom constraint name :

@Entity
@Table(uniqueConstraints = {
        @UniqueConstraint(columnNames = { "name" }, name = "name_constraint"),
        @UniqueConstraint(columnNames = { "teamId", "orderIndex" }, name = "teamId_orderIndex_constraint")
})
public class Space {
    // .. other field omitted

    private String name;

    private Integer orderIndex;

    private Integer teamId;
}

JPA alter the constraint with our custom name :

Hibernate: 
    alter table space
       add constraint name_constraint unique (name)

Hibernate: 
    alter table space
       add constraint teamId_orderIndex_constraint unique (team_id, order_index)

Properly handle DataIntegrityViolationException :

Using e.getMessage() , we get result back, which contains the name of the constraint we defined above:

could not execute statement; SQL [n/a]; constraint [space.name_constraint]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement

Then simply check if the result contains the name we defined:

import org.springframework.dao.DataIntegrityViolationException;

try {
    repository.save(space);
} catch (DataIntegrityViolationException e) {

    if(e.getMessage().contains("name_constraint")) {
        System.out.println("Unique name constraint violated");
    }

    if(e.getMessage().contains("teamId_orderIndex_constraint")) {
        System.out.println("Unique teamId_orderIndex constraints violated");
    }
}

Reference: Defining Unique Constraints in JPA

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM