我是Slick和Scala的新手。首先看看我的示例表,其中包含案例类映射和查询SuitsManager
的帮助器。现在,SuitsManager
的方法被Play调用! DBAction中的控制器(我正在使用play-slick 0.6.0.1)。
package models
import play.api.db.slick._
import play.api.db.slick.Config.driver.simple._
import scala.collection.immutable.HashMap
import scala.slick.jdbc.JdbcBackend
case class Suit(id:Option[Long],
complainant: String,
defender: String,
litigation: Long,
litigationValue: BigDecimal,
status: Long)
class Suits(tag: Tag) extends Table[Suit](tag, "SUITS") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def complainant = column[String]("complainant")
def defender = column[String]("defender")
def litigation = column[Long]("litigation")
def litigationValue = column[BigDecimal]("litigationValue")
def status = column[Long]("status")
def * = (id.?, complainant, defender, litigation, litigationValue, status) <> (Suit.tupled, Suit.unapply)
}
object SuitsManager {
val suits = TableQuery[Suits]
def list(offset: Int, limit: Int, filter: String = "%")(implicit session: JdbcBackend#Session) = {
suits.filter(_.defender like "%").drop(offset).take(limit).list
}
def count(offset: Int, limit: Int, filter: String = "%") : Long = {
suits.filter(_.defender like "%").drop(offset).take(limit).length.run
}
}
现在看看SuitsController
中第一个编译的2个方法,因为它声明了隐式会话参数。第二个给出了编译错误:
could not find implicit value for parameter session: play.api.db.slick.Config.driver.Backend#Session
因此,为查询创建辅助对象似乎不是很优雅。如果没有声明隐式会话参数,还有其他方法吗?也许使用导入?我的第二个问题:会话参数类型JdbcBackend#Session
是否正确?为什么不只是Session?
答案 0 :(得分:7)
第一个问题:
无法以某种方式避免绕过Session
。
您传递的隐式Session
不是您的应用程序的某些全局信息。 Session
对象表示您与数据库的当前打开会话。使用Play Slick,当向DBAction
发出请求时,将为您打开此数据库会话。
这意味着您的Session
仅可用,并且严格依赖于HTTP请求。事实上,您会发现它是implicit request =>
中每个DBAction
中注明的字段:
val someAction = DBAction { implicit request => // DBAction opens database session, and puts it in request.dbSession
// Database session for this request is implicitly available on
// the scope here and therefore may be passed to other methods implicitly
} // Database session is closed
因此,每个请求都有一个新的,不同的数据库会话。此外,每个数据库交互都需要数据库会话。因此,执行某些查询的每个方法都需要提供当前用于处理特定请求的Session
。
通常使用implicits的原因是因为它提供了传递此会话的最干净的代码。
// With implicits
def helperMethod1(param: Any)(implicit s: Session) = someQuery1.list // Session is passed implicitly
def helperMethod2(param: Any)(implicit s: Session) = someQuery2.exists.run // Session is passed implicitly
def action = DBAction { implicit request =>
// Stuff
helperMethod1(param1) // request.dbSession is passed implicitly
// Stuff
helperMethod2(param2) // request.dbSession is passed implicitly
// Stuff
}
与重复性更强
// Without implicits
def helperMethod1(param: Any, s: Session) = someQuery.list(s) // Must pass Session explicitly
def helperMethod2(param: Any, s: Session) = someQuery.exists.run(s)
def action = DBAction { implicit request =>
val session = request.dbSession
// Stuff
helperMethod1(param1, session) // Have to repeat session for every DB call
// Stuff
helperMethod2(param2, session)
// Stuff
}
@cvogt提到的Slick文档问题中的示例不是解决问题的好方法,因为它不会减少在这种情况下需要传递的参数数量。
第二个问题:
通常,Session
是JdbcBackend#Session
的所谓类型别名。这意味着type Session = JdbcBackend#Session
,即它们完全相同。您可以安全地在任何代码中使用Session
,但不幸的是,Play控制器代码。
原因是Play控制器还定义了类型Session
。 Play的Session
代表当前的HTTP会话,基于用户设置的cookie。不幸的是,这个命名与Slick的Session
冲突。
要解决此问题,您可以为导入设置别名:import scala.slick.driver.JdbcProfile.simple.{Session => SlickSession}
。
答案 1 :(得分:1)
如果重复困扰你,你可以把它变成一个帮助类,并将会话作为类的参数。这里解释了:http://slick.typesafe.com/doc/2.0.1/connection.html#passing-sessions-around
您建议的导入解决方案也存在,但它的类型安全性较低。请参阅:http://slick.typesafe.com/doc/2.0.1/connection.html#dynamically-scoped-sessions
会话是Slick驱动程序蛋糕中的路径依赖类型(如蛋糕模式),这就是需要通过#运算符
访问它的原因。