包装器捕获 db.run 调用中出现的异常

Wrapper catching exceptions accured on db.run call

在我们的项目中,我们总是在我们的 bean 对象中注入 dbConfigProvider: DatabaseConfigProvider,然后使用 db.run(some query) 进行数据库操作,它 returns 未来。我如何为 db.run 编写日志包装器,它将打印所有 sql 异常。

示例:

class SomeBeanImpl @Inject()(dbConfigProvider: DatabaseConfigProvider) {
  private val logger = Logger(getClass)

  def someDBQuery() = {
    db.run(some wrong sql query) // exception raised in future, I need to print it with logger
  }
}

注: 如果我在每个 db.run 调用上添加 .onFailure,它会把我的代码搞得一团糟。这就是为什么我需要为所有 db.run 调用编写这个包装器。

如果我将 db.run 包装在一些具有不同签名的函数中,我必须在很多地方进行更改,这不是最佳选择。我怎样才能隐含地做到这一点?

制作一个新的class:

  case class DBWrapper(db: DatabaseComponent) {
     def run(query: String) = db.run(query).onFailure { case e => logger.error(e) } 
  }

并用 DBWrapper(db) 替换你的 db。 您也可以隐式地来回转换,但在这种情况下我不推荐这样做。

您不需要显式创建一个新的包装器 class,您可以使用 Pimp My Library 模式创建一个隐式方法来包装 db.run 的调用并附加一个onFailureFuture:

object MyExtensions {
  class DbExtensions(db: Db) {
    def runAndLog(query: String): Future[String] = {
      val result = db.run(query)
      result.onFailure {
        case e => Logger.getLogger("x").error(s"Exception: $e")
      }
      result
    }
  }
  implicit def dbExtention(db: Db): DbExtensions = new DbExtensions(db)
}

class Db {
  def run(query: String): Future[String] = Future.successful("Hello")
}

object App extends Application {
  import MyExtensions._
  val db = new Db
  db.runAndLog("hello")
}

对于 Scala 2.10 及更高版本,可以使用 Implicit Classes:

显着缩短
implicit class DbExtensions(val db: Db) {
  def runAndLog(query: String): Future[String] = {
    val result = db.run(query)
    result.onFailure {
      case e => Logger.getLogger("x").error(s"Exception: $e")
    }
    result
  }
}

class Db {
  def run(query: String): Future[String] = Future.successful("Hello")
}

object App extends Application {
  val db = new Db
  db.runAndLog("hello")
}

您可以进一步使 DbExtensions 扩展 AnyVal 以进行性能优化:

implicit class DbExtensions(val db: Db) extends AnyVal