如何使用函数式编程风格管理Scala中的DB连接?

如何使用函数式编程风格管理Scala中的DB连接?,scala,functional-programming,database-connection,Scala,Functional Programming,Database Connection,我有一段使用DB连接的Scala代码: def getAllProviderCodes()(implicit conf : Configuration) : List[String] = { var conn: java.sql.Connection = null try { conn = DriverManager.getConnection(DBInfo.dbUrl(conf), DBInfo.dbUserName(conf), DBInfo.dbPassword(conf)

我有一段使用DB连接的Scala代码:

def getAllProviderCodes()(implicit conf : Configuration) : List[String] = {
  var conn: java.sql.Connection = null
  try {
    conn = DriverManager.getConnection(DBInfo.dbUrl(conf), DBInfo.dbUserName(conf), DBInfo.dbPassword(conf))
    return ResultSetIterator.create(
              conn.prepareStatement("SELECT pcode FROM providers").executeQuery()
           ){_.getString("pcode")}.toList
  } catch {
    case e: Exception =>
      logger.warn("Something went wrong with creating the connection: " + e.getStackTrace)
  } finally {
    if (conn != null) {
      conn.close()
    }
  }
  List()
}
这是一种非常类似于面向对象Java的风格,所以我想知道是否有一种方法可以用更实用的方式编写它?我试图成功地应用monad,但失败了:我最担心的是这里有state,以及
finally
块。也许这类案件有某种模式

先谢谢你

UPD:以下是解决方案的示例:

val connection = database.getConnection()
val data: Seq[Data] = Try{
  val results = connection.query("select whatever")
  results.map(convertToWhatIneed)
} recover {
  case t: Throwable => 
    Seq.empty[Data]
} get
connection.close()

但正如我在评论中提到的,我必须关闭连接,然后我必须把所有与连接有关的东西放在里面,尽量保持它的纯净。。。然后我转到try块中带有“try catch finally”的变体。

只需将连接拉出try:

 val conn = getConnection()
 try {
    doStuff(conn)
 } finally { 
    conn.close
 }
如果您希望整个过程的结果是一次尝试,只需将其包装成一次尝试:

 def doDBStuff = Try {
   val conn = getConnection()
   try {
    doStuff(conn)
   } finally { 
    conn.close
   }
 }
或者使用更少的嵌套(但这将引发连接异常):


我从未使用过Java SQL连接库,因此我的答案的语法是以伪代码的形式编写的,但是如果我正确理解了您的问题,那么我将如何实现您所做的工作:

def getAllProviderCodes()(implicit conf : Configuration): List[String] = {
  val conn: Connection = DriverManager.getConnection(???) // replace ??? with parameters

  val result: List[String] = Try {
    ??? // ResultSetIterator stuff
  } match {
    case Success(output) => output // or whatever .toList thing
    case Failure(_) => List.empty // add logging here
  }

  if(conn != null) conn.close()
  result // will be whatever List you make (or an empty List if Try fails)
}

与Java类的try catch finally块不同,Scala类的处理方法是将可能爆炸的东西放入try块中,并使用case Success(out)和case Failure(ex)将响应分配给一个值

你能使用
scala.util发布失败的代码吗?试试看
,这样我们就可以帮助你看到哪里出了问题?@JamesWhiteley实际上我没有失败的代码,因为我只是想知道如何实现它。我以为应该是类似的东西,但当我必须关闭连接时,我被卡住了,然后明白所有与连接有关的东西都应该放在尝试中。。。然后返回带有“try catch finally”的变量,仅在try块内:)
def getAllProviderCodes()(implicit conf : Configuration): List[String] = {
  val conn: Connection = DriverManager.getConnection(???) // replace ??? with parameters

  val result: List[String] = Try {
    ??? // ResultSetIterator stuff
  } match {
    case Success(output) => output // or whatever .toList thing
    case Failure(_) => List.empty // add logging here
  }

  if(conn != null) conn.close()
  result // will be whatever List you make (or an empty List if Try fails)
}