以优雅的方式传递Slick 2.0隐式会话

Seb*_*ian 1 session scala implicit playframework slick

我是Slick和Scala的新手.首先看一下我的示例表,其中包含用于查询的case类映射和帮助器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
  }
}
Run Code Online (Sandbox Code Playgroud)

现在看一下SuitsController第一个编译中的2个方法,因为它声明了隐式会话参数.第二个给出编译错误:

could not find implicit value for parameter session: play.api.db.slick.Config.driver.Backend#Session
Run Code Online (Sandbox Code Playgroud)

因此,为查询创建辅助对象似乎不是很优雅.如果没有声明隐式会话参数,还有其他方法吗?也许使用导入?我的第二个问题:会话参数类型JdbcBackend#Session是否正确?为什么不只是Session?

DCK*_*ing 7

第一个问题: 不可能避免Session以某种方式绕过.

Session您传递的隐含不是您的应用程序的一些全局信息.该Session对象表示您与数据库的当前打开会话.使用Play Slick,当向a发出请求时,将为您打开此数据库会话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
Run Code Online (Sandbox Code Playgroud)

因此,每个请求都有一个新的,不同的数据库会话.此外,每个数据库交互都需要数据库会话.因此,每个执行某些查询的方法都需要提供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
}
Run Code Online (Sandbox Code Playgroud)

而且重复性更强

// 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
}
Run Code Online (Sandbox Code Playgroud)

@cvogt提到的Slick文档问题中的示例不是解决问题的好方法,因为它不会减少在这种情况下需要传递的参数数量.

第二个问题: 一般来说,Session是一个所谓的类型别名JdbcBackend#Session.这意味着type Session = JdbcBackend#Session,即它们完全相同.您可以安全地使用Session任何代码,但不幸的是,Play控制器代码.

原因是Play控制器也定义了一种类型Session.Play's Session代表当前的HTTP会话,基于用户设置的cookie.不幸的是,这个命名与Slick的冲突Session.

要解决此问题,您可以为导入设置别名:import scala.slick.driver.JdbcProfile.simple.{Session => SlickSession}.