I am fairly new to JPA and want to find best practices when handling persistence exceptions from JPA for things like say, unique constraint violations which can be
I don’t like the idea of doing a query to check if the data can probably be inserted because it adds round-trips for a check the database server does anyway and it does not even work in every case because the database can change between the SELECT and the INSERT (though this may depend on how you handle transactions).
Anyway, handling the error looks like the only safe option to me, it’s “free” (no redundant checks, no additional round-trips) and it’s not hard to do. But it depends on your JDBC driver. For example, with PostgreSQL you can do:
try {
em.persist(credentials);
} catch (javax.persistence.PersistenceException ex) {
// use a loop to get the PSQLException
for (Throwable current = ex; current != null; current = current.getCause()) {
if (current instanceof PSQLException) {
final PSQLException psqlEx = (PSQLException) current;
final ServerErrorMessage serverErrorMessage = psqlEx.getServerErrorMessage();
if ("EMAIL_UQ_IDX".equals(serverErrorMessage.getConstraint())) {
// handle duplicate E-Mail address
}
break;
}
}
}
ServerErrorMessage (Javadoc, source code) provides a lot of information (which is used to generate the exception message):
System.out.println(serverErrorMessage.getColumn());
System.out.println(serverErrorMessage.getConstraint());
System.out.println(serverErrorMessage.getDatatype());
System.out.println(serverErrorMessage.getDetail());
System.out.println(serverErrorMessage.getFile());
System.out.println(serverErrorMessage.getHint());
System.out.println(serverErrorMessage.getInternalPosition());
System.out.println(serverErrorMessage.getInternalQuery());
System.out.println(serverErrorMessage.getLine());
System.out.println(serverErrorMessage.getMessage());
System.out.println(serverErrorMessage.getPosition());
System.out.println(serverErrorMessage.getRoutine());
System.out.println(serverErrorMessage.getSQLState());
System.out.println(serverErrorMessage.getSchema());
System.out.println(serverErrorMessage.getSeverity());
System.out.println(serverErrorMessage.getTable());
System.out.println(serverErrorMessage.getWhere());
When you do checks in a trigger, you can set many of these fields yourself by using the USING option = expression syntax, for example
RAISE integrity_constraint_violation USING CONSTRAINT = 'EMAIL_UQ_IDX'