comment gérez-vous et analysez-vous les exceptions de persistance JPA pour fournir un message significatif à l'utilisateur
je suis assez nouveau à JPA et je veux trouver les meilleures pratiques dans le traitement des exceptions de persistance de JPA pour des choses comme, par exemple, les violations de contraintes uniques qui être traité par l'utilisateur. Il ya des tonnes d'exemples sur la façon d'écrire apps JPA, mais presque rien sur la façon de remettre les exceptions éjectées par eux. :/
par exemple enregistrer un utilisateur, la personne entre une adresse e-mail qui est déjà active par le système et obtient une contrainte violation:
try {
em.persist(credentials);
} catch (javax.persistence.PersistenceException ex) {
qui produit cette erreur quand un email dupliqué est ajouté:
WARNING: SQL Error: 0, SQLState: 23505
SEVERE: ERROR: duplicate key value violates unique constraint "EMAIL_UQ_IDX"
Detail: Key (email)=(testuser@xyz.com) already exists.
Comment puis-je obtenir une réponse significative à l'utilisateur? Par exemple quelque chose comme: Oops ressemble à quelqu'un utilise déjà cette adresse e-mail, êtes-vous sûr que vous n'avez pas enregistré avant? Est-ce qu'il y a une facilité intégrée pour analyser ceci ou est-ce que je vais devoir exécuter des regexes contre le message d'exception dans un (peut-être une série de) IF statement(s)?
Et si c'est pris au niveau des affaires... quelles sont les pratiques exemplaires pour passer au niveau de la présentation?.. comme je l'ai déjà dit, pour qu'un message "gentil" puisse être fourni à l'utilisateur.
ajouté par souci de clarté: Juste pour que les gens sachent, j'AI, et je suis toujours en train de regarder tous les différents types d'exceptions de persistance, et voici quelques-unes des recherches que j'ai fait je n'ai pas inclus avec l'exemple "essai de déclaration" que j'ai inclus ci-dessus:
try {
em.persist(credentials);
} catch (javax.persistence.PersistenceException ex) {
System.out.println("EXCEPTION CLASS NAME: " + ex.getClass().getName().toString());
System.out.println("THROWABLE CLASS NAME: " + ex.getCause().getClass().getName().toString());
Throwable th = ex.getCause();
System.out.println("THROWABLE INFO: " + th.getCause().toString());
Logger.getLogger(CredentialsControllerImpl.class
.getName()).log(Level.INFO, "Credentials Controller "
+ "persistence exception "
+ "EXCEPTION STRING: {0}", ex.toString());
Logger.getLogger(CredentialsControllerImpl.class
.getName()).log(Level.INFO, "Credentials Controller "
+ "persistence exception "
+ "THROWABLE MESSAGE: {0}", th.getMessage());
Logger.getLogger(CredentialsControllerImpl.class
.getName()).log(Level.INFO, "Credentials Controller "
+ "persistence exceptions "
+ "THROWABLE STRING: {0}", th.toString());
}
:)
5 réponses
vous n'utilisez généralement pas les exceptions de bas niveau pour faire cela.
au lieu de cela, vous vérifiez explicitement que l'email est disponible (en utilisant une requête), et lisez l'email seulement s'il n'existe pas.
bien sûr, il pourrait y avoir une condition de course si deux threads font la même vérification en parallèle, mais ce sera extrêmement rare, et la contrainte de base de données est là pour garantir l'unicité.
il existe des sous-classes de Persistencenceexception: EntityExistsException, EntityNotFoundException, NonUniqueResultException, Noresulttexception, OptimisticLockException, RollbackException, TransactionRequiredException.
Source:
Vous pouvez les utiliser. Essayez de vérifier le type d'exception ou de surcharge d'une méthode de traitement des erreurs (qui est mieux). EntityExistsException je pense que l'erreur que vous recherchez dans l'exemple que vous avez donné ci-dessus. Mais vous devriez vérifier "si elle existe" vous-même. C'est la meilleure pratique.
l'Erreur SQL ne devrait jamais être nécessaire pour être montrée à l'utilisateur. Cette erreur est toujours pour vous. Toute erreur liée aux données qui nécessite d'informer l'utilisateur doit être vérifiée manuellement.
j'utilise L'environnement Web J2EE. Je transmets juste la demande à l'erreur.jsp s'il y a une exception. Je fournis aussi un objet supplémentaire pour erreur.jsp pour clarifier la les informations comme l'utilisateur peut revenir en arrière,peut aller quelle page après l'erreur etc. Bien sûr, je l'ai automatisé, Je n'aime pas écrire du code redondant, parce qu'il est difficile de le mettre à jour. Donc j'écris juste envoyer le message d'exception et d'erreur à une autre classe dans le bloc catch.
intégrez votre projet JPA avec Spring et laissez spring jeter les exceptions comme DataAccessException
sous-classes de DataAccessException
org.springframework.dao
class CannotAcquireLockException
Exception thrown on failure to aquire a lock during an update, for example during a "select for update" statement.
class CannotSerializeTransactionException
Exception thrown on failure to complete a transaction in serialized mode due to update conflicts.
class CleanupFailureDataAccessException
Exception thrown when we couldn't cleanup after a data access operation, but the actual operation went OK.
class ConcurrencyFailureException
Exception thrown on concurrency failure.
class DataAccessResourceFailureException
Data access exception thrown when a resource fails completely: for example, if we can't connect to a database using JDBC.
class DataIntegrityViolationException
Exception thrown when an attempt to insert or update data results in violation of an integrity constraint.
class DataRetrievalFailureException
Exception thrown if certain expected data could not be retrieved, e.g.
class DeadlockLoserDataAccessException
Generic exception thrown when the current process was a deadlock loser, and its transaction rolled back.
class EmptyResultDataAccessException
Data access exception thrown when a result was expected to have at least one row (or element) but zero rows (or elements) were actually returned.
class IncorrectResultSizeDataAccessException
Data access exception thrown when a result was not of the expected size, for example when expecting a single row but getting 0 or more than 1 rows.
class IncorrectUpdateSemanticsDataAccessException
Data access exception thrown when something unintended appears to have happened with an update, but the transaction hasn't already been rolled back.
class InvalidDataAccessApiUsageException
Exception thrown on incorrect usage of the API, such as failing to "compile" a query object that needed compilation before execution.
class InvalidDataAccessResourceUsageException
Root for exceptions thrown when we use a data access resource incorrectly.
class OptimisticLockingFailureException
Exception thrown on an optimistic locking violation.
class PermissionDeniedDataAccessException
Exception thrown when the underlying resource denied a permission to access a specific element, such as a specific database table.
class PessimisticLockingFailureException
Exception thrown on a pessimistic locking violation.
class TypeMismatchDataAccessException
Exception thrown on mismatch between Java type and database type: for example on an attempt to set an object of the wrong type in an RDBMS column.
class UncategorizedDataAccessException
Normal superclass when we can't distinguish anything more specific than "something went wrong with the underlying resource": for example, a SQLException from JDBC we can't pinpoint more precisely.
je fais quelque chose de similaire dans ma couche de service DB pour déterminer si l'exception a été causée par une contrainte conflictuelle ou une défaillance générale de la base de données:
try {
....
} catch (final PersistenceException e) {
final Throwable cause = e.getCause();
if (cause instanceof MySQLIntegrityConstraintViolationException) {
throw new ConflictException(cause);
}
throw new ServiceException(e);
}
je n'aime pas l'idée de faire une requête pour vérifier si les données peuvent probablement être inséré parce qu'il ajoute des aller-retours pour vérifier que le serveur de base de données n'a de toute façon et il n'a même pas de travail dans tous les cas, parce que la base de données peut changer entre le SELECT
et INSERT
(bien que cela puisse dépendre de la façon dont vous traitez les transactions).
quoi qu'il en soit, gérer l'erreur ressemble à la seule option sûre pour moi, il est "libre" (pas de contrôles redondants, pas de voyages aller-retour supplémentaires) et il n'est pas difficile de faire. Mais cela dépend de votre pilote JDBC. Par exemple, avec PostgreSQL vous pouvez faire:
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 fournit beaucoup d'informations (qui sont utilisées pour générer le message d'exception):
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());