2014-11-22 15 views
12

La mia tabella ha un indice univoco su una coppia di colonne nel mio database postgresql.Come catturare le eccezioni postgres slick per le violazioni duplicate dei valori delle chiavi

voglio sapere come posso prendere un'eccezione di chiave duplicata quando sono inserite le seguenti:

def save(user: User)(implicit session: Session): User = { 
    val newId = (users returning users.map(_id) += user 
    user.copy(id = newId) 
} 

miei log mostrano questa eccezione:

Execution exception[[PSQLException: ERROR: duplicate key value violates unique constraint "...." 

non ho davvero eccezioni tanto in usato scala o ancora anche.

risposta

21

Il metodo save dovrebbe probabilmente restituire qualcosa di diverso dal numero User, per indicare la possibilità di errore. Se l'unica eccezione che verrà lanciata è la chiave univoca, e in realtà ti interessa solo il successo o l'insuccesso (e non il tipo di errore), un modo per andare sarebbe quello di restituire Option[User].

Si potrebbe utilizzare un semplice try/catch blocco, la mappatura di successo salva Some[User] e PSQLException-None:

def save(user: User)(implicit session: Session): Option[User] = { 
    try { 
    val newId = (users returning users.map(_id) += user 
    Some(user.copy(id = newId)) 
    } catch { 
     case PSQLException => None 
    } 
} 

Personalmente non il modo in cui mi piacerebbe andare, come try/catch non è davvero idiomatica Scala, e il vostro errore il tipo viene scartato. L'opzione successiva è usare scala.util.Try.

def save(user: User)(implicit session: Session): Try[User] = Try { 
    val newId = (users returning users.map(_id) += user 
    user.copy(id = newId) 
} 

Il codice qui è più semplice. Se il corpo di Try ha esito positivo, quindi save restituirà Success[User] e in caso contrario restituirà l'eccezione racchiusa in Failure. Questo ti permetterà di fare molte cose con Try.

Si potrebbe pattern match:

save(user) match { 
    case Success(user) => Ok(user) 
    case Failure(t: PSQLException) if(e.getSQLState == "23505") => InternalServerError("Some sort of unique key violation..") 
    case Failure(t: PSQLException) => InternalServerError("Some sort of psql error..") 
    case Failure(_) => InternalServerError("Something else happened.. it was bad..") 
} 

si potrebbe usare come Option:

save(user) map { user => 
    Ok(user) 
} getOrElse { 
    InternalServerError("Something terrible happened..") 
} 

È possibile comporre molti insieme in una sola volta, e fermarsi al primo errore:

(for { 
    u1 <- save(user1) 
    u2 <- save(user2) 
    u3 <- save(user3) 
} yield { 
    (u1, u2, u3) 
}) match { 
    case Success((u1, u2, u3)) => Ok(...) 
    case Failure(...) => ... 
} 
+0

wow, informazioni fantastiche, grazie. Giusto per chiarire, per individuare una "chiave duplicata" dovrei analizzare il messaggio di stringa restituito dal messaggio per capire se si trattava di una chiave duplicata? (come opposto ad altre psqlexecptions). – Blankman

+1

O rispettando lo stato SQL. Non utilizzo postgres, quindi non sono sicuro che sia accurato al 100% (mysql utilizza i codici di errore), ma ad esempio 'case e: PSQLException if (e.getSQLState ==" 23505 ") => ... 'Vedere questa pagina: http://www.postgresql.org/docs/current/static/errcodes-appendix.html –

+0

corrisponde all'output di Try to scalaz disgiunzioni ed è possibile modellare il contratto dei metodi in modo corretto.rende un'ottima API che è facile da usare con la domanda novizio –

13

In Slick 3.x è possibile utilizzare asTry.

Sto usando MySQL, ma lo stesso codice può essere utilizzato per PostgreSQL, solo le eccezioni sono diverse.

import scala.util.Try 
import scala.util.Success 
import scala.util.Failure 

db.run(myAction.asTry).map {result => 

    result match { 

    case Success(res) => 
     println("success") 
     // ... 

    case Failure(e: MySQLIntegrityConstraintViolationException) => { 
     //code: 1062, status: 23000, e: Duplicate entry 'foo' for key 'name' 
     println(s"MySQLIntegrityConstraintViolationException, code: ${e.getErrorCode}, sql status: ${e.getSQLState}, message: ${e.getMessage}") 
     // ... 
    } 

    case Failure(e) => { 
     println(s"Exception in insertOrUpdateListItem, ${e.getMessage}") 
     // ... 
    } 
    } 
} 

Nota: E 'anche possibile mappare l'azione (myAction.asTry.map ...) al posto del futuro restituito da db.run.

+0

quali sono le classi 'DBResult' e' DBStatus'? Non li vedo nello scaladoc slick – ps0604

+0

Queste sono le mie classi. "Uso anche un wrapper dei risultati con un codice di stato personalizzato per tutti i risultati del mio database.". Li rimuoverò per evitare confusione. – Ixx

Problemi correlati