如何捕获重复键值违规的光滑postgres异常

Bla*_*man 12 scala playframework slick

我的表在postgresql数据库中的一对列上有一个唯一索引.

我想知道在插入时如何捕获重复的键异常:

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

我的日志显示此异常:

Execution exception[[PSQLException: ERROR: duplicate key value violates unique constraint "...."
Run Code Online (Sandbox Code Playgroud)

我还没有在scala中使用异常.

Mic*_*jac 27

你的save方法应该返回不同的东西User,以表明失败的可能性.如果将抛出的唯一异常是由唯一键,并且您真的只关心成功或失败(而不是失败的类型),一种方法是返回Option[User].

你可以使用一个简单的try/catch块,映射成功保存到Some[User]PSQLExceptionNone:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

个人不是我去的方式,因为try/catch不是真正的惯用Scala,你的错误类型被丢弃.下一个选项是使用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)
}
Run Code Online (Sandbox Code Playgroud)

这里的代码更简单.如果正文Try成功,那么save将返回Success[User],如果不成功则返回包含的异常Failure.这将允许你做很多事情Try.

你可以模式匹配:

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..")
}
Run Code Online (Sandbox Code Playgroud)

您可以使用它Option:

save(user) map { user =>
   Ok(user)
} getOrElse {
   InternalServerError("Something terrible happened..")
}
Run Code Online (Sandbox Code Playgroud)

你可以一次组合很多,并在第一次失败时停止:

(for {
   u1 <- save(user1)
   u2 <- save(user2)
   u3 <- save(user3)
} yield {
  (u1, u2, u3)
}) match {
   case Success((u1, u2, u3)) => Ok(...)
   case Failure(...) => ...
}
Run Code Online (Sandbox Code Playgroud)


Ixx*_*Ixx 16

在Slick 3.x中你可以使用asTry.

我正在使用MySQL,但是相同的代码可以用于PostgreSQL,只有异常是不同的.

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}")
      // ...
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

注意:也可以映射action(myAction.asTry.map ...)而不是返回的future db.run.