对于练习来说,我已经从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} }
^
答案 0 :(得分: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
,同时关闭一切都需要关闭。
答案 1 :(得分: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))
从一行中读取数据库。
答案 2 :(得分: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)
}