我的表在postgresql数据库中的一对列上有唯一索引。
我想知道在插入时如何捕获重复的键异常:
def save(user: User)(implicit session: Session): User = {
val newId = (users returning users.map(_id) += user
user.copy(id = newId)
}
我的日志显示此异常:
Execution exception[[PSQLException: ERROR: duplicate key value violates unique constraint "...."
我还没有真正在scala中使用异常。
答案 0 :(得分:26)
您的save
方法应该返回与User
不同的内容,以指示失败的可能性。如果抛出的唯一异常是由唯一键,并且您真的只关心成功或失败(而不是失败的类型),那么一种方法是返回Option[User]
。
您可以使用简单的try/catch
块,将成功保存映射到Some[User]
和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
}
}
个人不是我的方式,因为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)
}
这里的代码更简单。如果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..")
}
您可以像Option
:
save(user) map { user =>
Ok(user)
} getOrElse {
InternalServerError("Something terrible happened..")
}
你可以一次组合很多,并在第一次失败时停止:
(for {
u1 <- save(user1)
u2 <- save(user2)
u3 <- save(user3)
} yield {
(u1, u2, u3)
}) match {
case Success((u1, u2, u3)) => Ok(...)
case Failure(...) => ...
}
答案 1 :(得分:15)
在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}")
// ...
}
}
}
注意:也可以映射操作(myAction.asTry.map ...
)而不是db.run
返回的未来。