有没有一种简单的方法可以摆脱anorm的隐式连接样板?
我有一个DB对象:
import java.sql.Connection
import scalikejdbc.ConnectionPool
object DB {
def withConnection[A](block: Connection => A): A = {
val connection: Connection = ConnectionPool.borrow()
try {
block(connection)
} finally {
connection.close()
}
}
}
然后所有查询必须被此模式包围
def myMethod(par1: Int, par2: String): Seq[MyClass] = {
DB.run SQL("SELECT a,b,c FROM table WHERE foo={par1} AND bar={par2}")
.on('par1=par1, 'par2=par2)
.as(MyClass.myRowParser *)
}
在DB上使用这个Connection => A
取消函数implicit connection
的方法会很好,所以我可以写简单:
def myMethod(par1: Int, par2: String): Seq[MyClass] = {
DB.run SQL("SELECT a,b,c FROM table WHERE foo={par1} AND bar={par2}")
.on('par1=par1, 'par2=par2)
.as(MyClass.myRowParser *)
}
有没有办法轻松做到这一点?
答案 0 :(得分:1)
取决于你想要去兔洞的距离......在我的一些DAO代码中,我使用类似的东西定义一个免费的monad over SQL操作...首先定义一个包装泛型的case类连接到A的函数:
final case class SQLOperation[A](block: Connection ⇒ A)
然后,为它定义一个仿函数:
implicit object SQLOperationFunctor extends Functor[SQLOperation] {
def map[A, B](a: SQLOperation[A])(f: A ⇒ B) = SQLOperation[B]((c:Connection) ⇒ f(a.block(c)))
}
然后,为它定义一个免费的monadic类型(你可以使用Scalaz Free类型,只要你定义了一个函子):
type FreeSQLOperation[A] = Free[SQLOperation, A]
为了让事情变得简单,您可以直接定义一个隐含的提升操作到Free monad:
implicit def liftSQLOperationToFree[A](op : SQLOperation[A]) : FreeSQLOperation[A] = {
Free.liftF(op)
}
一旦你完成了所有这些机制的定义,你就需要为自由monadic动作定义一个解释器,就像这样,它为你的执行提供了一个指挥和控制的中心点:
final def run[A](t : ⇒ FreeSQLOperation[A]) : SQLResult[A] = t.fold(
(a: A) ⇒ a.right,
(op : SQLOperation[FreeSQLOperation[A]]) ⇒ {
DB.withConnection { implicit c ⇒
val t= Try(run(op.block(c)))
t match {
case scala.util.Success(a) ⇒ a
case scala.util.Failure(ex) ⇒ ex.getMessage.left
}
}
}
)
在这种情况下,所有内容都包含在Try中,以便集中处理异常,然后映射到Validation或Disjunction类型。 (这就是SQLResult类型......)
一旦你把所有这些打到一起,在你的DAO课程中,你就可以添加一些这样的通用方法,例如:
def selectAll(params : ⇒ Seq[NamedParameter]) : FreeSQLOperation[List[V]] = {
val clause = params.map(p ⇒ s"${p.name} = {${p.name}}").mkString(" and ")
SQLOperation( implicit c ⇒
SQL(s"""
| select * from $source
| where $clause
""".stripMargin).on(params : _*).as(rowParser *).flatten
)
}
或者这个:
def insert(params : ⇒ Seq[NamedParameter]) : FreeSQLOperation[Int] = {
val columns = params.map(p ⇒ s"${p.name}").mkString(",")
val values = params.map(p ⇒ s"{${p.name}}").mkString(",")
SQLOperation( implicit c ⇒
SQL(s"""
| insert into $source ($columns)
| values ($values)
""".stripMargin).on(params : _*).executeInsert(scalar[Int].single)
)
}
您可以放在基类中。将run方法添加到基类(解释器位),然后在更高级别的DAO对象中,您可以编写基本上如下所示的代码:
override def read(key: String): SQLResult[Principal] = {
run {
selectOne {
Seq("uid" → key)
}
}
}
或者,你可以看到Free monad如何允许你将操作链接在一起:
run {
for {
m1 ← updateByUid(uid){Seq("uid" → value.uid)}
m2 ← updateByUid(value.uid){Seq("secret" → value.secret)}
m3 ← updateByUid(value.uid){Seq("modified" → DateTime.now)}
p ← selectOne { Seq("uid" → value.uid) }
} yield (p)
}
有很多繁忙的工作要设置,但是一旦你有了,你的实际业务逻辑变得更加清晰,杂乱无章,这也意味着你可以交换一个不同的解释器(定义运行函数)如果你想采用不同的策略来处理异常,记录等......
HTH