避免Scala + JDBC中的可变变量

时间:2017-12-27 18:30:09

标签: scala

我在Scala中有以下代码来使用JDBC访问数据库。它工作正常,但它使用了几个可变变量(var声明),因为这些变量需要在finally子句中可用,才能关闭JDBC元素。可以将此代码更改为仅使用不可变变量(val声明)吗?

var connection:Connection = null
var statement:Statement = null
var resultSet:ResultSet = null

try {
  val url = "someUrl"
  val user = "someUser"
  val password = "somePwd"
  Class.forName("mySql")
  connection = DriverManager.getConnection(url, user, password)
  statement = connection.createStatement()
  resultSet = statement.executeQuery("SELECT column FROM table")
  while ( resultSet.next() ) {
    val col = resultSet.getString(1)
    println(col)
  }

} 
catch {
  case e:Exception => e.printStackTrace
}
finally {
  if (resultSet != null) resultSet.close
  if (statement != null) statement.close
  if (connection != null) connection.close
}

4 个答案:

答案 0 :(得分:4)

  • 您可以创建小功能来返回连接。
  • 当您没有值时,您不必在顶部声明变量(例如<script> var $groupeCompetence = $('#requete_prestataire_groupeCompetence'); // When sport gets selected ... $groupeCompetence.change(function() { // ... retrieve the corresponding form. var $form = $(this).closest('form'); // Simulate form data, but only include the selected sport value. var data = {}; data[$groupeCompetence.attr('name')] = $groupeCompetence.val(); // Submit data via AJAX to the form's action path. $.ajax({ url : $form.attr('action'), type: $form.attr('method'), data : data, success: function(html) { // Replace current position field ... $('#requete_prestataire_competence').replaceWith( // ... with the returned one from the AJAX response. $(html).find('#requete_prestataire_competence') ); // Position field now displays the appropriate positions. } }); }); </script> public function testIndexRechercheUtilisateurNonConnecte() { $crawler = $this->client->request('GET', '/'); $form = $crawler->selectButton('requete_prestataire_Rechercher')->form(); $form['requete_prestataire[groupeCompetence]'] = 2; $form['requete_prestataire[competence]'] = ""; $crawler = $this->client->submit($form); $this->assertTrue($this->client->getResponse()->isRedirect()); $client->followRedirect(); /*$this->assertEquals(3, $crawler->filter('a [class = "btn-sm btn-primary"]')->count());*/ } connection)他们。相反,您可以创建函数以将实际值返回为statement

  • 或者,因为您需要引用resultSetOption[T]等来关闭它们,this answer has nice explanation关闭资源。我在这里窃取相同的代码。查看函数connection,它接受​​您想要处理的资源以及在清理资源之后运行的代码。

  • 另请参阅收集statement数据的不可变方式。

所以,你会看起来像,

autoClean

注意:代码已编译但我没有针对数据库运行。

答案 1 :(得分:2)

这是一个相对直接的翻译,你已经进入了更多的FP。为清晰起见,添加了注释。 (我没有运行它,但确实编译了。)

import scala.util.Try

val connection:Try[Connection]= Try(DriverManager.getConnection(url, user, password))
val statement: Try[Statement] = connection.map(_.createStatement())
val resultSet: Try[ResultSet] = statement.map(_.executeQuery("SELECT column FROM table"))

resultSet.map(rs => while (rs.next()) println(rs.getString(1)))  
         .recover{case e => e.printStackTrace()}

resultSet.foreach(_.close())
statement.foreach(_.close())
connection.foreach(_.close())

我们的想法是通过使失败条件成为变量类型的一部分来避免var

在这种情况下,如果createStatement()失败,您将不会尝试getConnection(),如果executeQuery()失败,您将不会尝试createStatement(),并且你close()只有那些没有失败的资源。

答案 2 :(得分:1)

这是一种基于Scala的Try和Stream类的方法。它建立在jwvh的答案之上,通过添加异常处理来确保即使在错误情况下所有资源也会关闭。

这种方法通过将异常捕获到Scala的Try抽象中来避免var,并通过将JDBC行处理转换为Scala集合来避免while循环。

尝试可以保存两个状态,一个成功的值或一个例外。  只有tryInstance的值保存成功值时,才会调用tryInstance.map。

Scala Stream是懒惰的,即他们一次计算一个值并延迟计算下一个值。这允许我们将while循环移动到Scala集合库中,代价是有一些额外的逻辑来检测流的末尾,从而在正确的时间关闭资源。

注意,此代码尚未经过测试,但我已在生产代码中成功使用此策略。为了便于说明,我在此处包含以下代码。

import Stream._

Class.forName("mySql")

def doQuery( url:String, user:String, password:String ): List[String] = {
  val conn      = Try(DriverManager.getConnection(url, user, password))
  val statement = conn.map( _.createStatement() )
  val rs        = statement.map( _.executeQuery("SQL") )

  toStream(rs,statement,conn).toList
}

private def toStream(rs:Try[ResultSet], s:Try[Statement], c:Try[Connection]) : Stream[String] = {
  try {
    val resultSet = rs.get // will throw an exception if any previous problems had occurred while opening the connection and parsing the sql

    if ( resultSet.next() ) {
      resultSet.getString(1) #:: toStream(rs,s,c)
    } else {
      close(rs,s,c)

      Stream.empty
    }
  } catch {
    case ex =>
      close(rs,s,c)
      throw ex
  }
}

答案 3 :(得分:0)

我看到有很多锅炉板代码,如获得连接,关闭连接,处理故障!这就是Java应该如何完成的,但是你想写Scala。因此,如果不直接使用JDBC库是一个选项,您可以尝试使用一些映射库为您做这个锅炉板。

例如,你可以看看Slick,它可以帮助你编写数据库抽象,保持功能范式的完整!