Anorm隐式连接样板
Anorm implicit connection boilerplate
有没有一种简单的方法可以摆脱异常的隐式连接样板文件?
我有一个数据库对象:
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 上有一个方法可以用这个 implicit connection
取消函数 Connection => A
的需要,这样我就可以简单地写:
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 *)
}
有没有办法轻松做到这一点?
取决于你想深入兔子洞多远...在我的一些 DAO 代码中,我在 SQL 操作上定义了一个 Free monad,使用类似这样的东西...首先定义一个case class 包装了一个通用函数,该函数接受一个连接到 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)))
}
然后,为它定义一个 Free monadic 类型(你可以使用 Scalaz Free 类型 class,只要你定义了一个仿函数):
type FreeSQLOperation[A] = Free[SQLOperation, A]
为了让事情更简单,您可以定义一个隐式的提升操作到自由 monad 中:
implicit def liftSQLOperationToFree[A](op : SQLOperation[A]) : FreeSQLOperation[A] = {
Free.liftF(op)
}
一旦你定义了所有的机制,你就需要为 Free 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 中,以便集中处理异常,然后映射到验证或析取类型。 (这就是 SQL 结果类型...)
一旦你把所有这些都放在一起,在你的 DAO classes 中,你就可以添加一些像这样的通用方法,例如:
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)
)
}
您可以将其放入基数 class。将 运行 方法添加到基础 class(解释器位),然后在更高级别的 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
有没有一种简单的方法可以摆脱异常的隐式连接样板文件?
我有一个数据库对象:
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 上有一个方法可以用这个 implicit connection
取消函数 Connection => A
的需要,这样我就可以简单地写:
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 *)
}
有没有办法轻松做到这一点?
取决于你想深入兔子洞多远...在我的一些 DAO 代码中,我在 SQL 操作上定义了一个 Free monad,使用类似这样的东西...首先定义一个case class 包装了一个通用函数,该函数接受一个连接到 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)))
}
然后,为它定义一个 Free monadic 类型(你可以使用 Scalaz Free 类型 class,只要你定义了一个仿函数):
type FreeSQLOperation[A] = Free[SQLOperation, A]
为了让事情更简单,您可以定义一个隐式的提升操作到自由 monad 中:
implicit def liftSQLOperationToFree[A](op : SQLOperation[A]) : FreeSQLOperation[A] = {
Free.liftF(op)
}
一旦你定义了所有的机制,你就需要为 Free 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 中,以便集中处理异常,然后映射到验证或析取类型。 (这就是 SQL 结果类型...)
一旦你把所有这些都放在一起,在你的 DAO classes 中,你就可以添加一些像这样的通用方法,例如:
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)
)
}
您可以将其放入基数 class。将 运行 方法添加到基础 class(解释器位),然后在更高级别的 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