Slick 3.1.1查询类型的上限

时间:2016-05-20 14:29:16

标签: slick slick-3.0

我有一个DAO帮助程序特征,它为DAO提供了通用功能。它需要能够访问表查询并运行操作。我无法定义或以其他方式向帮助程序特征提供查询类型。

以下是action分支中的一些代码,也可以在短demo project on GitHub中找到。 首先,db在特征DBComponent中定义:

trait DBComponent {
  import slick.driver.JdbcProfile

  val driver: JdbcProfile
  import driver.api._

  val db: Database
}

要保留的类扩展HasId

trait HasId {
  def id: Option[Int] = None
}

这是一个要保留的类:

case class BankInfo(
  owner: String,
  branches: Int,
  bankId: Int,
  override val id: Option[Int] = None
) extends HasId

问题是我不知道如何在以下DAO助手特性中设置QueryType;我希望随后的大多数错误都是我使用的不正确类型的结果:

/** Handles all actions pertaining to HasId or that do not require parameters */
trait DbAction[T <: HasId] { this: DBComponent =>
  import driver.api._ // defines DBIOAction

  type QueryType <: slick.lifted.TableQuery[Table[T]] // this type is wrong! What should it be?
  def tableQuery: QueryType

  // Is this defined correctly?
  def run[R](action: DBIOAction[R, NoStream, Nothing]): Future[R] = db.run { action }

  def deleteById(id: Option[Long]): Unit =
    for { i <- id } run { tableQuery.filter(_.id === id).delete } // id is unknown because QueryType is wrong

  def findAll: Future[List[T]] = run { tableQuery.to[List].result } // also b0rked

  // Remaining methods shown on GitHub
}

仅供参考,以下是如何使用上述内容。首先,定义表查询的特征:

trait BankInfoTable extends BankTable { this: DBComponent =>
  import driver.api._

  class BankInfoTable(tag: Tag) extends Table[BankInfo](tag, "bankinfo") {
    val id       = column[Int]("id", O.PrimaryKey, O.AutoInc)
    val owner    = column[String]("owner")
    val bankId   = column[Int]("bank_id")
    val branches = column[Int]("branches")

    def bankFK = foreignKey("bank_product_fk", bankId, bankTableQuery)(_.id)

    def * = (owner, branches, bankId, id.?) <> (BankInfo.tupled, BankInfo.unapply)
  }

  val tableQuery = TableQuery[BankInfoTable]

  def autoInc = tableQuery returning tableQuery.map(_.id)
}

这一切都在这里:

trait BankInfoRepositoryLike extends BankInfoTable with DbAction[BankInfo]
{ this: DBComponent =>

  import driver.api._

  @inline def updateAsync(bankInfo: BankInfo): Future[Int] =
    run { tableQuery.filter(_.id === bankInfo.id.get).update(bankInfo) }

  @inline def getByIdAsync(id: Int): Future[Option[BankInfo]] =
    run { tableQuery.filter(_.id === id).result.headOption }
}

建议?

2 个答案:

答案 0 :(得分:2)

完整的工作示例:

 package com.knol.db.repo

import com.knol.db.connection.DBComponent
import com.knol.db.connection.MySqlDBComponent
import scala.concurrent.{Await, Future}
import concurrent.duration.Duration

trait LiftedHasId {
  def id: slick.lifted.Rep[Int]
}

trait HasId {
  def id: Option[Int]
}

trait GenericAction[T <: HasId]{this: DBComponent =>
  import driver.api._

  type QueryType <: slick.lifted.TableQuery[_ <: Table[T] with LiftedHasId]

  val tableQuery: QueryType

  @inline def deleteAsync(id: Int): Future[Int] = db.run { tableQuery.filter(_.id === id).delete }
  @inline def delete(id: Int): Int = Await.result(deleteAsync(id), Duration.Inf)

  @inline def deleteAllAsync(): Future[Int] = db.run { tableQuery.delete }
  @inline def deleteAll(): Int = Await.result(deleteAllAsync(), Duration.Inf)

  @inline def getAllAsync: Future[List[T]] = db.run { tableQuery.to[List].result }
  @inline def getAll: List[T] = Await.result(getAllAsync, Duration.Inf)

  @inline def getByIdAsync(id: Int): Future[Option[T]] =
    db.run { tableQuery.filter(_.id === id).result.headOption }

  @inline def getById(id: Int): Option[T] = Await.result(getByIdAsync(id), Duration.Inf)

  @inline def deleteById(id: Option[Int]): Unit =
    db.run { tableQuery.filter(_.id === id).delete }

  @inline def findAll: Future[List[T]] = db.run { tableQuery.to[List].result }




}
trait BankInfoRepository extends BankInfoTable  with GenericAction[BankInfo] { this: DBComponent =>

  import driver.api._

  type QueryType  = TableQuery[BankInfoTable]

  val tableQuery=bankInfoTableQuery

  def create(bankInfo: BankInfo): Future[Int] = db.run { bankTableInfoAutoInc += bankInfo }

  def update(bankInfo: BankInfo): Future[Int] = db.run { bankInfoTableQuery.filter(_.id === bankInfo.id.get).update(bankInfo) }

  /**
   * Get bank and info using foreign key relationship
   */
  def getBankWithInfo(): Future[List[(Bank, BankInfo)]] =
    db.run {
      (for {
        info <- bankInfoTableQuery
        bank <- info.bank
      } yield (bank, info)).to[List].result
    }

  /**
   * Get all bank and their info.It is possible some bank do not have their product
   */
  def getAllBankWithInfo(): Future[List[(Bank, Option[BankInfo])]] =
    db.run {
      bankTableQuery.joinLeft(bankInfoTableQuery).on(_.id === _.bankId).to[List].result
    }
}

private[repo] trait BankInfoTable extends BankTable{ this: DBComponent =>

  import driver.api._

  class BankInfoTable(tag: Tag) extends Table[BankInfo](tag, "bankinfo") with LiftedHasId {
    val id = column[Int]("id", O.PrimaryKey, O.AutoInc)
    val owner = column[String]("owner")
    val bankId = column[Int]("bank_id")
    val branches = column[Int]("branches")
    def bank = foreignKey("bank_product_fk", bankId, bankTableQuery)(_.id)
    def * = (owner, branches, bankId, id.?) <> (BankInfo.tupled, BankInfo.unapply)

  }

  protected val bankInfoTableQuery = TableQuery[BankInfoTable]

  protected def bankTableInfoAutoInc = bankInfoTableQuery returning bankInfoTableQuery.map(_.id)

}

object BankInfoRepository extends BankInfoRepository with MySqlDBComponent

case class BankInfo(owner: String, branches: Int, bankId: Int, id: Option[Int] = None) extends HasId

答案 1 :(得分:1)

您尝试使用HasId对结果类型进行抽象,但您的代码实际上并不关心这一点。您正在使用的id值来自提升类型,即表行类,因此您需要在此级别进行抽象:

trait LiftedHasId {
  def id: slick.lifted.Rep[Int]
}

然后在DbAction

type QueryType <: slick.lifted.TableQuery[_ <: Table[T] with LiftedHasId]

BankInfoTable必须为它定义具体类型:

type QueryType = slick.lifted.TableQuery[BankInfoTable]

或者您可以将其作为第二个类型参数添加到DbAction(就像Query有两个类型参数,用于提升类型结果类型)。