Che è meglio – Lanciare un’eccezione o verificare preventivamente gli errori

Nel mio server, che è connesso a postgresql, dovrei controllare se il nome utente esiste già nella tabella facendo "select * ..." e poi ottenere il numero di righe nel resultset e io il numero di righe è uguale a zero , quindi inserisci il nome utente?
O semplicemente inserisci il nome utente nella tabella. Se esiste già, genererà un errore che può quindi essere rilevato.
Nota: il nome utente è la chiave primaria

Fare quale dei due sopra è meglio?

Dovresti fare il metodo “try-and-catch exception” semplicemente perché devi farlo comunque.

Se controlli prima, non c’è nulla che impedisca a qualcuno di inserire una riga per quell’utente tra il tuo assegno e il tuo inserto, nel qual caso l’utente sarà nella tabella anche se il tuo assegno non l’ha trovato.

A corto di essere in grado di eseguire il check-and-insert all’interno di una sorta di transazione (in modo che nessun altro possa inserire quell’utente nel frattempo). non puoi essere certo che la non-eccezione funzionerà.

E sebbene molti DBMS forniscano supporto transazionale, non ne conosco nessuno che bloccherà una riga che devi ancora inserire 🙂

Naturalmente, se la tua applicazione è progettata in modo tale che solo il tuo processo inserirà gli utenti (e serializzato), puoi utilizzare il metodo check-first. Ma dovrei inserire molti commenti che dovrebbero essere rivisitati se mai dovessi aumentare.

Il consueto consenso è di utilizzare le eccezioni solo per casi eccezionali e non come un costrutto di stream di controllo. Cercando di usare un nome utente che capita di essere presa dovrebbe, a mio parere, essere considerato un caso d’uso valido e non così raro.

In altre parole, vorrei prima controllare i nomi utente esistenti.

Come nota @paxdiablo, tuttavia, se ci si trova in un ambiente con multithreading, ad esempio un server Web, è necessario aggiungere uno schema di blocco o comunque utilizzare l’approccio try / catch (considerando che due thread potrebbero essere in competizione per aggiungere lo stesso nome utente). ). Questa situazione può tuttavia essere considerata un caso eccezionale.

Domande correlate (tutte con la stessa conclusione, non usare eccezioni per casi non eccezionali):

  • Le eccezioni sono davvero per errori eccezionali?
  • C ++: ci sono motivi per usare le eccezioni in situazioni non eccezionali
  • Giudicare se un’eccezione è eccezionale
  • Perché usare un’eccezione anziché se … altro

In questo caso la risposta non è Né provocare un errore, né verificare in anticipo. Beh, per lo più , comunque.
Può essere gestito più semplice – e più sicuro e più veloce allo stesso tempo:

 INSERT INTO users(username, col1) SELECT 'max', 'val1' WHERE NOT EXISTS (SELECT * FROM users WHERE username = 'max') 

Questo inserirà il nuovo utente solo se non esiste già. PostgreSQL imposterà lo stato del comando su 0 rows affected o su 1 row affected , a seconda che sia già presente. Ad ogni modo, sarà lì dopo questa affermazione.

Se vuoi una risposta indietro:

 INSERT INTO users(username, col1) SELECT 'max', 'val1' WHERE NOT EXISTS (SELECT * FROM users WHERE username = 'max') RETURNING username; 

Questo restituirà il nome utente solo se non esiste già.
Tuttavia, l’operazione non è atomica, quindi se hai un sacco di concorrenza, acquisisci un blocco sulla tabella in questo modo:

 BEGIN; LOCK TABLE users IN SHARE MODE; INSERT INTO users(username, col1) SELECT 'max', 'val1' WHERE NOT EXISTS (SELECT * FROM users WHERE username = 'max') RETURNING username; COMMIT; 

Nota che questo può ancora fallire, anche se molto improbabile – ad esempio se un’altra transazione blocca la tabella e ti blocca per sempre a causa di qualche errore.
Quindi, ammettiamolo, hai ancora bisogno del codice per gestire il caso di errore. Non dovrebbe mai accadere a meno che il database o le applicazioni non abbiano un problema.

  • Tom Lane sull’argomento.
  • Il manuale sulle serrature.

Direi che l’osservazione di un’eccezione in questo caso è l’abuso del concetto di eccezione, se puoi verificarlo prima, dovresti controllarlo.

le eccezioni non dovrebbero mai essere utilizzate per controllare il stream del programma. È consigliabile evitare le eccezioni se non si tratta di un caso esemplificativo.

Preferirei un controllo per l’utente esistente fatto tramite query piuttosto che usare l’eccezione. La logica per l’errore “utente esistente” potrebbe presto diventare una regola aziendale. (beh, puoi scrivere tali regole in SQL, ma questo è un mondo completamente diverso)

O semplicemente inserisci il nome utente nella tabella. Se esiste già, genererà un errore che può quindi essere rilevato

Il problema è che ci sono molti altri motivi di eccezione. Devi comunque gestirli.

Riceverai molte risposte sulla falsariga di “le eccezioni non dovrebbero mai essere usate per controllare il stream del programma” e “le eccezioni non sono per il stream di controllo”. In effetti ne hai già diversi. Puoi, come me, trovare queste affermazioni del tutto prive di significato. Le eccezioni controllano il stream del programma e quando un’API è progettata per generare un’eccezione, non si ha altra scelta che usarla di conseguenza. EOFException è un esempio EOFException . Quando si chiamano i metodi che lo lanciano, non si ha alcun altro modo di rilevare una EOS.

In questo caso si applica un principio diverso. Se testate e poi impostate, state introducendo una finestra di temporizzazione durante la quale il set successivo può fallire comunque, e se l’operazione set può generare un’eccezione dovete comunque codificarla. In queste situazioni dovresti semplicemente fare il set e gestire l’eccezione di conseguenza. In questo modo l’operazione è atomica e non è necessario scrivere lo stesso codice due volte. In generale, il modo più affidabile per rilevare se una risorsa è disponibile è provare a utilizzarlo (prendere in considerazione la connessione a un server di rete) e il modo più affidabile per rilevare se un’operazione fallirà è effettivamente provarlo (si consideri questo caso, cioè inserendo un valore in una struttura dati in cui è una chiave univoca).

La regola su “le eccezioni non dovrebbero mai essere usate per controllare il stream del programma” originariamente proveniva da un contesto molto più ristretto, il che significa che non dovresti in generale generare eccezioni che catturi nello stesso metodo, cioè usale come una sorta di GOTO . Tuttavia, come è molto comune in questo settore, la motivazione originale è stata interamente dimenticata a favore di ciò che posso solo descrivere come una ripetizione senza senso, a forma di pappagallo.

Se si inserisce il nome utente direttamente senza alcun controllo e si suppone che lo stesso nome utente esista nel database, in quel momento si otterrà un’eccezione altrimenti inserirà il record correttamente nel database. Come da prassi di codifica standard (dal mio punto di vista), dovresti controllare prima l’unicità e se il nome utente non esiste nel database, inserisci record nel database.

La cosa giusta da fare non riguarda la gestione delle eccezioni nel tuo caso, dovresti usare una chiave primaria che incrementa automaticamente.

Autoincremento PostgreSQL