2

作为练习,我已经从大约 100 行中得到了一些简单的 JDBC 内容,但它不会进行类型检查。有任何想法吗?更好的方法?

def withResultSet[T](sql: String, f: ResultSet => T)(implicit info: ConnectionInfo): Try[T] = {
    for {
      conn <- Try(connect(info))
      stmt <- Try(conn.createStatement()) orElse { case err: SQLException => {conn.close(); err} }
      results <- Try(stmt.executeQuery(sql)) orElse { case err: SQLException => { conn.close(); stmt.close(); err }}
    } yield f(results)
  }

我的错误是

 missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: scala.util.Try[?]
      stmt <- Try(conn.createStatement()) orElse { case err: SQLException => {conn.close(); err} }
                                                 ^
4

3 回答 3

3

我不知道这Try是在不再需要资源时处理资源的正确工具。至少我看不到一个明显的方法来做到这一点。您可能想查看https://github.com/jsuereth/scala-arm。那么您的代码可能如下所示:

def withResultSet[T](sql: String, f: ResultSet => T)(
                     implicit info: ConnectionInfo): ManagedResource[T] = for {
  conn <- managed(connect(info))
  stmt <- managed(conn.createStatement()) 
  results <- managed(stmt.executeQuery(sql))
} yield f(results)

您可以继续使用 map 或 flatMap(或用于理解)来处理可能的其他资源,最后您可以从中获得一个Option或一个Either,同时关闭所有需要关闭的东西。

于 2013-08-06T04:34:42.573 回答
2

您可以将函数拆分为不同的部分,每个部分对应一种资源类型,并将其与单独的资源管理器特征一起使用。

通用资源管理器(也可以与文件等一起使用):

trait ResourceManager {
  def withResource[T <: {def close()}, R](resource: T)(code: (T) => R): R = {
    try {
      code(resource)
    } finally {
      import scala.language.reflectiveCalls
      resource.close()
    }
  }

混合成这样的东西

class Db extends ResourceManager {
  private def getConnection = ...

  def withConnection[T](f: (Connection) => T): T = {
    withResource(getConnection) {
      conn =>
        f(conn)
    }
  }

  def withStatement[T](f: (Statement) => T): T = {
    withConnection {
      conn =>
        withResource(conn.createStatement()) {
          stmnt =>
            f(stmnt)
        }
    }
  }

  def withResultSet[T](selectSqlCmd: String)(f: (ResultSet) => T): T = {
    withStatement {
      stmnt => {
        withResource(stmnt.executeQuery(selectSqlCmd)) {
          rs =>
            f(rs)
        }
      }
    }
  }
}

它堆叠资源,并在每个级别给出入口点。

在此之上的另一个层次

  def mapResultSet[T, C <: Iterable[T]](selectSqlCmd: String)
                              (f: (ResultSet) => T)
                              (implicit cbf: CanBuildFrom[Nothing, T, C]): C = {
    withResultSet(selectSqlCmd) {
      rs =>
        val builder = cbf()
        while (rs.next()) {
          builder += f(rs)
        }
        builder.result()
    }
  }

每种方法都只向下走一步,for理解被分解为单独的函数,并且Try不干涉。

像这样使用它:

val db = new Db()
val result = db.mapResultSet("select * from pg_user")(rs => rs.getString(1))

在一行中从数据库中读取。

于 2013-08-06T08:11:49.520 回答
1

recoverWith是我需要的;你实际上不需要恢复你可以重新抛出。

def withResultSet[T](sql: String, f: ResultSet => T)(implicit info: ConnectionInfo): Try[T] = {
    for {
      conn <- Try(connect(info))
      stmt <- Try(conn.createStatement()) recoverWith { case err => {conn.close(); Failure(err)} }
      results <- Try(stmt.executeQuery(sql)) recoverWith { case err => { conn.close(); stmt.close(); Failure(err) }}
    } yield f(results)
  }
于 2013-08-06T04:33:20.643 回答