簡體   English   中英

使用Slick Table的Scala類型推斷

[英]Scala type inference working with Slick Table

有這樣的模型(簡化):

case class User(id:Int,name:String)
case class Address(id:Int,name:String)
...

Slick(2.1.0版)表映射:

class Users(_tableTag: Tag) extends Table[User](_tableTag, "users") with WithId[Users, User] {`
  val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey)
  ...
}
trait WithId[T, R] {
  this: Table[R] =>
  def id: Column[Int]
}

混合特征WithId我想為列id: Column[Int]為不同的表實現通用DAO方法id: Column[Int] (我希望方法findById能夠同時UserAddress表映射)

trait GenericSlickDAO[T <: WithId[T, R], R] {
  def db: Database

  def findById(id: Int)(implicit stk: SlickTableQuery[T]): Option[R] = db.withSession { implicit session =>
    stk.tableQuery.filter(_.id === id).list.headOption
  }

trait SlickTableQuery[T] {
  def tableQuery: TableQuery[T]
}

object SlickTableQuery {
  implicit val usersQ = new SlickTableQuery[Users] {
    val tableQuery: Table Query[Users] = Users
  }
}

問題是findById無法編譯:

錯誤:(13,45)類型不匹配; found:需要選項[T#TableElementType]:Option [R] stk.tableQuery.filter(_。id === id).list.headOption

我認為T的類型為WithId[T, R] ,同時類型為Table[R] Slick實現Table類型,如果X=Table[Y]X#TableElementType=Y

所以在我的情況下, T#TableElementType=ROption[T#TableElementType]應該被推斷為Option[R]但事實並非如此。 我哪里錯了?

你對WithId[T, R]屬於Table[R]類型的假設是錯誤的。 WithId[T, R]的自我類型注釋只需要混合Table[R] ,但這並不意味着WithId[T, R]Table[R]

我想你混淆的聲明WithId與實例WithId最終需要一個實例Table

您在綁定約束上型GenericSlickDAO特質也不能保證你的財產WithId是實例Table ,因為任何類型的本身就是一個亞型。

有關自我類型和子類型之間差異的更詳細解釋,請參閱問題。

我正在使用Play-slick,我試着像你一樣做,有一個特點,並使用self-type沒有成功。

但我成功了以下幾點:

import modelsunscanned.TableWithId

import scala.slick.jdbc.JdbcBackend
import scala.slick.lifted.TableQuery
import play.api.db.slick.Config.driver.simple._


/**
 * @author Sebastien Lorber (lorber.sebastien@gmail.com)
 */
package object models {

  private[models] val Users = TableQuery(new UserTable(_))
  private[models] val Profiles = TableQuery(new ProfileTable(_))
  private[models] val Companies = TableQuery(new CompanyTable(_))
  private[models] val Contacts = TableQuery(new ContactTable(_))


  trait ModelWithId {
    val id: String
  }


  trait BaseRepository[T <: ModelWithId] {
    def tableQuery: TableQuery[TableWithId[T]]


    private val FindByIdQuery = Compiled { id: Column[String] =>
      tableQuery.filter(_.id === id)
    }


    def insert(t: T)(implicit session: JdbcBackend#Session) = {
      tableQuery.insert(t)
    }


    def getById(id: String)(implicit session: JdbcBackend#Session): T = FindByIdQuery(id).run.headOption
      .getOrElse(throw new RuntimeException(s"Could not find entity with id=$id"))

    def findById(id: String)(implicit session: JdbcBackend#Session): Option[T] = FindByIdQuery(id).run.headOption




    def update(t: T)(implicit session: JdbcBackend#Session): Unit = {
      val nbUpdated = tableQuery.filter(_.id === t.id).update(t)
      require(nbUpdated == 1,s"Exactly one should have been updated, not $nbUpdated")
    }

    def delete(t: T)(implicit session: JdbcBackend#Session) = {
      val nbDeleted = tableQuery.filter(_.id === t.id).delete
      require(nbDeleted == 1,s"Exactly one should have been deleted, not $nbDeleted")
    }

    def getAll(implicit session: JdbcBackend#Session): List[T] = tableQuery.list

  }

}


// play-slick bug, see https://github.com/playframework/play-slick/issues/227
package modelsunscanned {
   abstract class TableWithId[T](tableTag: Tag,tableName: String) extends Table[T](tableTag,tableName) {
    def id: Column[String]
  }
}

我給你一個例子:

object CompanyRepository extends BaseRepository[Company] {
  // Don't know yet how to avoid that cast :(
  def tableQuery = Companies.asInstanceOf[TableQuery[TableWithId[Company]]] 

  // Other methods here
  ...
}




case class Company(
                    id: String = java.util.UUID.randomUUID().toString,
                    name: String,
                    mainContactId: String,
                    logoUrl: Option[String],
                    activityDescription: Option[String],
                    context: Option[String],
                    employeesCount: Option[Int]
                    ) extends ModelWithId


class CompanyTable(tag: Tag) extends TableWithId[Company](tag,"COMPANY") {
  override def id = column[String]("id", O.PrimaryKey)
  def name = column[String]("name", O.NotNull)
  def mainContactId = column[String]("main_contact_id", O.NotNull)
  def logoUrl = column[Option[String]]("logo_url", O.Nullable)
  def activityDescription = column[Option[String]]("description", O.Nullable)
  def context = column[Option[String]]("context", O.Nullable)
  def employeesCount = column[Option[Int]]("employees_count", O.Nullable)
  //
  def * = (id, name, mainContactId,logoUrl, activityDescription, context, employeesCount) <> (Company.tupled,Company.unapply)
  //
  def name_index = index("idx_name", name, unique = true)
}

請注意, active-slick也使用類似的東西

這對我幫助很大。 這是genericdao https://gist.github.com/lshoo/9785645的一個非常簡單的例子

package slicks.docs.dao

import scala.slick.driver.PostgresDriver.simple._
import scala.slick.driver._


trait Profile {
  val profile: JdbcProfile
}

trait CrudComponent {
  this: Profile =>

  abstract class Crud[T <: Table[E] with IdentifiableTable[PK], E <: Entity[PK], PK: BaseColumnType](implicit session: Session) {

    val query: TableQuery[T]

    def count: Int = {
      query.length.run
    }

    def findAll: List[E] = {
      query.list()
    }

    def queryById(id: PK) = query.filter(_.id === id)

    def findOne(id: PK): Option[E] = queryById(id).firstOption

    def add(m: E): PK = (query returning query.map(_.id)) += m 

    def withId(model: E, id: PK): E 

    def extractId(m: E): Option[PK] = m.id

    def save(m: E): E = extractId(m) match {
      case Some(id) => {
        queryById(id).update(m)
        m
      }
      case None => withId(m, add(m))
    }

    def saveAll(ms: E*): Option[Int] = query ++= ms

    def deleteById(id: PK): Int = queryById(id).delete

    def delete(m: E): Int = extractId(m) match {
      case Some(id) => deleteById(id)
      case None => 0
    }

  }
}

trait Entity[PK] {
  def id: Option[PK]
}

trait IdentifiableTable[I] {
  def id: Column[I]
}


package slicks.docs

import slicks.docs.dao.{Entity, IdentifiableTable, CrudComponent, Profile}


case class User(id: Option[Long], first: String, last: String) extends Entity[Long]

trait UserComponent extends CrudComponent {
  this: Profile =>

  import profile.simple._

  class UsersTable(tag: Tag) extends Table[User](tag, "users") with IdentifiableTable[Long] {

    override def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def first = column[String]("first")
    def last = column[String]("last")

    def * = (id.?, first, last) <> (User.tupled, User.unapply)

  }

  class UserRepository(implicit session: Session) extends Crud[UsersTable, User, Long] {

    override def query = TableQuery[UsersTable]

    override def withId(user: User, id: Long): User = user.copy(id = Option(id))
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM