Slick 3.0 Generic CRUD实现中的类型参数绑定错误



我正在尝试为Scala play应用程序实现一个通用的CRUD特性。我遵循这个实现。

这些是我的依赖-

libraryDependencies ++= Seq(
    "org.postgresql" % "postgresql" % "9.4-1201-jdbc41",
    "com.typesafe.slick" %% "slick" % "3.0.0",
    "com.typesafe.play" %% "play-slick" % "1.0.1",
    "com.typesafe.play" %% "play-slick-evolutions" % "1.0.1",
    "io.spray" %%  "spray-json" % "1.3.2",
    specs2 % Test

)

这是我的通用模型-

package models.generic
import slick.driver.PostgresDriver.api._
import scala.concurrent.Future

abstract class GenericTable[T](tag: Tag, name: String) extends Table[T](tag, name) {
    def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
}
trait GenericCRUD[C <: GenericTable[T], T] extends configs.DBConfig{
    protected val table: TableQuery[C]
    private val queryById = Compiled((id: Rep[Int]) => table.filter(_.id === id))
    def all: Future[Seq[C#TableElementType]] = db.run(table.result)
    def create(c: C#TableElementType): Future[Int] = db.run(table += c)
    def read(id: Int): Future[Option[C#TableElementType]] = db.run(queryById(id).result.headOption)
    def update(id: Int, c: C#TableElementType): Future[Int] = db.run(queryById(id).update(c))
    def delete(id: Int): Future[Int] = db.run(queryById(id).delete)
}

这是使用泛型的模型实现-

package models
import java.sql.Timestamp
import scala.concurrent.Future
import slick.driver.PostgresDriver.api._
import generic.{GenericCRUD, GenericTable}

case class Brand(id: Int, name: String, created: Timestamp, updated: Timestamp)
class BrandTable(tag: Tag) extends GenericTable[Brand](tag, "brand") {
    override def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
    def name = column[String]("name")
    def created = column[Timestamp]("created")
    def updated = column[Timestamp]("updated")
    def * = (id, name, created, updated) <>((Brand.apply _).tupled, Brand.unapply)
}
object Brands extends GenericCRUD[Brand, BrandTable] {
    override val table:TableQuery[Brand] = TableQuery[Brand]
}

目前我得到以下错误-

play.sbt.PlayExceptions$CompilationException: Compilation error[type arguments [models.Brand] conform to the bounds of none of the overloaded alternatives of
 value apply: [E <: slick.lifted.AbstractTable[_]]=> slick.lifted.TableQuery[E] <and> [E <: slick.lifted.AbstractTable[_]](cons: slick.lifted.Tag => E)slick.lifted.TableQuery[E]]
        at play.sbt.PlayExceptions$CompilationException$.apply(PlayExceptions.scala:27) ~[na:na]
        at play.sbt.PlayExceptions$CompilationException$.apply(PlayExceptions.scala:27) ~[na:na]
        at scala.Option.map(Option.scala:145) ~[scala-library-2.11.7.jar:na]
        at play.sbt.run.PlayReload$$anonfun$taskFailureHandler$1.apply(PlayReload.scala:49) ~[na:na]
        at play.sbt.run.PlayReload$$anonfun$taskFailureHandler$1.apply(PlayReload.scala:44) ~[na:na]
        at scala.Option.map(Option.scala:145) ~[scala-library-2.11.7.jar:na]
        at play.sbt.run.PlayReload$.taskFailureHandler(PlayReload.scala:44) ~[na:na]
        at play.sbt.run.PlayReload$.compileFailure(PlayReload.scala:40) ~[na:na]
        at play.sbt.run.PlayReload$$anonfun$compile$1.apply(PlayReload.scala:17) ~[na:na]
        at play.sbt.run.PlayReload$$anonfun$compile$1.apply(PlayReload.scala:17) ~[na:na]
        at scala.util.Either$LeftProjection.map(Either.scala:377) ~[scala-library-2.11.7.jar:na]
        at play.sbt.run.PlayReload$.compile(PlayReload.scala:17) ~[na:na]
        at play.sbt.run.PlayRun$$anonfun$playRunTask$1$$anonfun$apply$2$$anonfun$apply$3$$anonfun$2.apply(PlayRun.scala:61) ~[na:na]
        at play.sbt.run.PlayRun$$anonfun$playRunTask$1$$anonfun$apply$2$$anonfun$apply$3$$anonfun$2.apply(PlayRun.scala:61) ~[na:na]
        at play.runsupport.Reloader$$anonfun$reload$1.apply(Reloader.scala:323) ~[na:na]
        at play.runsupport.Reloader$$anon$3.run(Reloader.scala:43) ~[na:na]
        at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_51]
        at play.runsupport.Reloader$.play$runsupport$Reloader$$withReloaderContextClassLoader(Reloader.scala:39) ~[na:na]
        at play.runsupport.Reloader.reload(Reloader.scala:321) ~[na:na]
        at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1.apply(DevServerStart.scala:113) ~[play-server_2.11-2.4.3.jar:2.4.3]
        at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1.apply(DevServerStart.scala:111) ~[play-server_2.11-2.4.3.jar:2.4.3]
        at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24) ~[scala-library-2.11.7.jar:na]
        at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24) ~[scala-library-2.11.7.jar:na]
        at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40) ~[akka-actor_2.11-2.3.13.jar:na]
        at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:397) ~[akka-actor_2.11-2.3.13.jar:na]
        at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) ~[scala-library-2.11.7.jar:na]
        at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) ~[scala-library-2.11.7.jar:na]
        at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) ~[scala-library-2.11.7.jar:na]
        at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) ~[scala-library-2.11.7.jar:na]

我发现了错误。我交换了泛型类型在GenericCRUD trait中的位置。以下是更正后的版本

通用模型:

package models
import java.sql.Timestamp
import scala.concurrent.Future
import slick.driver.PostgresDriver.api._
import generic.{GenericCRUD, GenericTable}

case class Brand(id: Int, name: String, created: Timestamp, updated: Timestamp)
class BrandTable(tag: Tag) extends GenericTable[Brand](tag, "brand") {
    override def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
    def name = column[String]("name")
    def created = column[Timestamp]("created")
    def updated = column[Timestamp]("updated")
    def * = (id, name, created, updated) <>((Brand.apply _).tupled, Brand.unapply)
}
object Brands extends GenericCRUD[BrandTable, Brand] {
    override val table = TableQuery[BrandTable]
}

实现模型:

package models.generic
import slick.driver.PostgresDriver.api._
import scala.concurrent.Future

abstract class GenericTable[T](tag: Tag, name: String) extends Table[T](tag, name) {
    def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
}
trait GenericCRUD[C <: GenericTable[T], T] extends configs.DBConfig{
    protected val table: TableQuery[C]
    private val queryById = Compiled((id: Rep[Int]) => table.filter(_.id === id))
    def all: Future[Seq[C#TableElementType]] = db.run(table.result)
    def create(c: C#TableElementType): Future[Int] = db.run(table += c)
    def read(id: Int): Future[Option[C#TableElementType]] = db.run(queryById(id).result.headOption)
    def update(id: Int, c: C#TableElementType): Future[Int] = db.run(queryById(id).update(c))
    def delete(id: Int): Future[Int] = db.run(queryById(id).delete)
}

我正在使用这个CRUD实现:

https://github.com/jose78/Dashboard-DB/blob/master/src/main/scala/com/github/elx/dashboard/db/TableConf.scala

 trait Crud[M] {
     import slick.lifted.TableQuery
     def generateScript[T , F ](callback:(TableQuery[_]) => String);
     def create(model: M): Any;
     def update(model: M): Future[Int];
     def findByID(id: Long): Future[Option[M]];
     def read(): Future[List[M]];
     def delete(model: M): Future[Int];
     def createTable(): Unit;
}

class CrudImpl[V, M <: DefaultModel[Option[Long]], T <: AbsTable[M]](tableQuery: TableQuery[T]) extends Crud[M] {
     ....    
}
val crudUser: Crud[User] = new CrudImpl[Option[Long], User, UserTable](TableQuery[UserTable]);

我希望你喜欢。

相关内容