我在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
}
答案 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
或者,因为您需要引用resultSet
,Option[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,它可以帮助你编写数据库抽象,保持功能范式的完整!