简体   繁体   English

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

[英]Passing Slick 2.0 implicit session in elegant way

I'm new to Slick and Scala. 我是Slick和Scala的新手。 First of take a look at my example table with case class mapping and helper for queries SuitsManager . 首先看一下我的带有案例类映射和查询SuitsManager帮助器的示例表。 Now methods of SuitsManager are called by Play! 现在, SuitsManager方法被Play调用! controllers inside DBAction (I'm using play-slick 0.6.0.1). DBAction内部的控制器(我使用的是平滑播放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
  }
}

Now take a look at 2 methods in SuitsController the first one compiles because it declares implicit session parameter. 现在看一下SuitsController的2个方法,第一个方法会编译,因为它声明了隐式会话参数。 The second one gives compile error: 第二个给出了编译错误:

could not find implicit value for parameter session: play.api.db.slick.Config.driver.Backend#Session

So creating helper objects for queries seems to be not very elegant. 因此,创建用于查询的助手对象似乎不是很优雅。 Is there any other way do it without declaring implicit session parameter? 还有其他方法可以不声明隐式会话参数吗? Maybe using an import? 也许使用导入? My second question: is the session parameter type JdbcBackend#Session correct? 我的第二个问题:会话参数类型JdbcBackend#Session正确吗? Why not just Session? 为什么不只是Session?

First question: It is not possible to avoid passing around the Session in some way. 第一个问题:无法避免以某种方式绕过Session

The implicit Session you pass around is not some global information of your application. 您传递的隐式Session不是您的应用程序的某些全局信息。 The Session object represents the current open session you have with the database. Session对象代表您与数据库的当前打开的会话。 With Play Slick, this database session is opened for you when a request is made to a DBAction . 使用Play Slick,当向DBAction发出请求时,将为您打开此数据库会话。

That means that your Session is only available, and strictly tied to, an HTTP request. 这意味着您的Session仅可用,并且严格绑定到HTTP请求。 In fact, you'll find that it is a field of the implicit request => you note in every DBAction : 实际上,您会发现它是每个DBAction 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

So you have a new and different database session for every request. 因此,每个请求都有一个新的不同的数据库会话。 Furthermore, every database interaction requires a database session. 此外,每次数据库交互都需要一个数据库会话。 Therefore, every method that executes some query therefore needs to be provided with the current Session you have for processing your particular request. 因此,每个执行某些查询的方法都需要随当前Session一起提供,以处理您的特定请求。

The reason implicits are usually used for this is because it provides the cleanest code for passing around this session. 通常使用隐式的原因是因为它提供了用于传递此会话的最干净的代码。

// 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
}

Versus the more repetitive 与更重复

// 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
}

The example in the Slick documentation question that @cvogt mentions is not a good solution to your problem, as it does not reduce the amount of arguments you need to pass around in this case. @cvogt提到的Slick文档问题中的示例不是解决您问题的好方法,因为在这种情况下,它不会减少您需要传递的参数数量。

Second question: In general, Session is a so-called type alias for for JdbcBackend#Session . 第二个问题:一般来说, SessionJdbcBackend#Session的所谓类型别名。 That means that type Session = JdbcBackend#Session , ie they're exactly the same. 这意味着type Session = JdbcBackend#Session ,即它们是完全相同的。 You can safely use Session in any code except, unfortunately, Play controller code. 您可以安全地以任何代码使用Session ,但不幸的是,Play控制器代码除外。

The reason for this is that Play controllers also define a type Session . 原因是Play控制器还定义了Session类型。 Play's Session represents the current HTTP session, based on the cookies the user has set. Play的Session表示基于用户设置的cookie的当前HTTP会话。 Unfortunately, this naming clashes with Slick's Session . 不幸的是,这种命名与Slick的Session冲突。

To work around this, you can alias the import: import scala.slick.driver.JdbcProfile.simple.{Session => SlickSession} . 要解决此问题,您可以为导入添加别名: import scala.slick.driver.JdbcProfile.simple.{Session => SlickSession}

You can make it a helper class and session an argument of the class, if the repetition bothers you. 如果重复使您感到困扰,则可以将其设置为帮助器类并为该类的一个会话进行会话。 It's explained here: http://slick.typesafe.com/doc/2.0.1/connection.html#passing-sessions-around 此处说明: http : //slick.typesafe.com/doc/2.0.1/connection.html#passing-sessions-around

The import-solution you suggested exists as well, but it is less typesafe. 您建议的导入解决方案也存在,但类型安全性较差。 See: http://slick.typesafe.com/doc/2.0.1/connection.html#dynamically-scoped-sessions 请参阅: http : //slick.typesafe.com/doc/2.0.1/connection.html#dynamically-scoped-sessions

Session is a path-dependent type in the Slick driver cake (as in cake pattern), that's why it needs to be accessed via the # operator 会话是Slick驱动程序蛋糕中与路径相关的类型(就像蛋糕模式一样),这就是为什么需要通过#运算符访问它的原因

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM