2012-05-08 16 views
24

Sono abbastanza nuovo per JPA e voglio trovare le migliori pratiche quando si gestiscono eccezioni di persistenza da JPA per cose come dire, violazioni di vincoli univoci che possono essere indirizzati a dall'utente. Ci sono un sacco di esempi su come scrivere app JPA, ma quasi nulla su come gestire le eccezioni lanciate da loro. :/come gestire e analizzare le eccezioni di persistenza JPA per fornire un messaggio significativo all'utente

Per esempio la registrazione di un utente, la persona entra un indirizzo email che è già in uso attivo da parte del sistema e ottiene una violazione di vincolo:

try { 
    em.persist(credentials); 
} catch (javax.persistence.PersistenceException ex) { 

che produce questo errore quando si aggiunge un'email duplicato :

WARNING: SQL Error: 0, SQLState: 23505 
SEVERE: ERROR: duplicate key value violates unique constraint "EMAIL_UQ_IDX" 
    Detail: Key (email)=([email protected]) already exists. 

Come posso ottenere una risposta significativa all'utente? Ad esempio qualcosa come: Oops sembra che qualcuno stia già usando quell'indirizzo email, sei sicuro di non esserti registrato prima? C'è una funzione integrata per analizzarla o dovrò eseguire espressioni regex rispetto al messaggio di eccezione in una (possibilmente una serie di) istruzioni?

E che dire se è catturato sul livello aziendale ... quali sono le migliori pratiche per sollevarlo fino al livello di presentazione ... come ho detto prima, in modo che possa essere fornito un messaggio "carino" per utente.


Aggiunto per chiarezza: Solo così la gente sa, ho avuto, avere, e sto ancora guardando tutti i diversi tipi di eccezioni di persistenza, ed ecco alcune delle ricerche che ho fatto io didn' t includono con l'esempio "provare affermazione" ho incluso sopra:

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

:)

risposta

4

In genere non utilizzare le eccezioni di basso livello per farlo.

Invece, si controlla esplicitamente che l'e-mail sia disponibile (utilizzando una query) e si interrompa l'e-mail solo se non esiste.

Certo, potrebbe esserci una condizione di competizione se due thread eseguono lo stesso controllo in parallelo, ma sarà estremamente raro e il vincolo del database è lì per garantire l'unicità.

+0

eccezioni come DataAccessException

sottoclassi di DataAccessException può l'annotazione @Version noi essere ed eliminare la condizione della gara? Come funzionerebbe? Avevo pensato di fare il check-up come suggerisci, ma le condizioni della gara mi preoccupavano e lo fanno ancora. Come dici tu, le possibilità sono basse; ma questo significa solo che accadrà anche se raramente, quindi deve ancora essere gestito. Ammetto che questo è uno dei rari casi d'uso in cui l'utente ha bisogno di essere coinvolto, soprattutto se si desidera consentire a un nome utente e al req'd indirizzo email entrambi di essere unici. FWIW, provengo dal mondo dei grandi volumi (100 delle transazioni K/DB di DB/sz), così divento ansioso sugli hit DB non necessari. :) – BillR

+0

Inoltre, suppongo sia ovvio che si verrebbe a creare un'eccezione di persistenza acquisita nel livello aziendale fino al livello di presentazione. Quando accade un errore di vincolo indirizzabile da parte dell'utente, questo è (come concordiamo anche se raramente ... il che significa che succederà: D). O probabilmente in tutti i casi e il livello di presentazione decide cosa chiede all'utente di correggere e cosa renderà una pagina di errore piacevole, anche se frustrante (per l'utente). Grazie per l'input, a proposito. – BillR

+1

La @Version servirebbe a qualsiasi scopo qui: entrambi i thread controllerebbero se esiste già un messaggio di posta elettronica, entrambi non lo troverebbero, ed entrambi proverebbero ad inserire. La condizione di competizione verrà risolta dal vincolo univoco nel database. In tal caso, si visualizza un messaggio di errore generico all'utente o si riprova automaticamente la transazione non riuscita. Il tentativo ha buone probabilità (soprattutto se si utilizza un piccolo ritardo prima) per scoprire che l'e-mail esiste e verrà visualizzato un messaggio di errore significativo per l'utente. –

3

Esistono sottoclassi di PersistenceException: EntityExistsException, EntityNotFoundException, NonUniqueResultException, NoResultException, OptimisticLockException, RollbackException, TransactionRequiredException. Fonte: http://docs.oracle.com/javaee/5/api/javax/persistence/PersistenceException.html

È possibile utilizzarli. Prova a verificare il tipo di eccezione o sovraccaricare un metodo di gestione degli errori (che è meglio). EntityExistsException Penso che l'errore che stai cercando nell'esempio che hai dato sopra. Ma dovresti controllare "se esiste" da solo. Questa è la migliore pratica.

L'errore SQL non dovrebbe mai essere necessario per essere mostrato all'utente. Questo errore è sempre per te. Qualsiasi errore relativo ai dati che necessita di informare l'utente deve essere controllato manualmente.

Uso l'ambiente Web J2EE. Devo solo inoltrare la richiesta a error.jsp se c'è un'eccezione. Fornisco anche un oggetto aggiuntivo per error.jsp per chiarire le informazioni come l'utente può tornare indietro, può andare quale pagina dopo errore ecc. Naturalmente l'ho automatizzato, non mi piace scrivere codice ridondante, perché è difficile da aggiornare.Quindi scrivo solo per inviare l'eccezione e il messaggio di errore ad un'altra classe nel blocco catch.

+0

Accetto il fatto che non si debba perdere il messaggio di errore SQL all'utente. Sebbene la corretta gestione degli errori dovrebbe essere implementata per fare la differenza tra ad esempio un errore interno del server o una richiesta errata (errore dell'utente). La pre-interrogazione per i conflitti non risolve questo problema poiché la condizione di competizione può ancora essere applicata. – Pepster

1

sto facendo qualcosa di simile nel mio livello di servizio DB per capire se l'eccezione è stata causata da un vincolo di conflitto o un fallimento generalizzato del DB:

try { 
.... 
} catch (final PersistenceException e) { 
    final Throwable cause = e.getCause(); 
    if (cause instanceof MySQLIntegrityConstraintViolationException) { 
     throw new ConflictException(cause); 
    } 
    throw new ServiceException(e); 
} 
3

integrare il progetto JPA con la Primavera e lasciare che la primavera gettare la in 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. 
+0

Un certo numero di anni fa ho provato Spring, e personalmente mi sono identificato con persone che si lamentavano di "XML Hell". Intendiamoci, odiavo l'originale Java EE (ed è per questo che capisco perché è nata Spring). Ma non potevo sopportare tanto il punto che non volevo avere nulla a che fare con Java. Da quando JEE6 sono tornato, ho trovato l'offerta JEE "ufficiale" più intuitiva e più pulita. Comunque non penso che tornerò a rivisitare Spring. – BillR

Problemi correlati