首页 > 解决方案 > 使用 Slick 插入具有自动递增 ID 的值的通用存储库/DAO 方法缺少隐式

问题描述

我对 scala 相当陌生,并试图学习slick并从 play-slick-example 开始,一切都是可以理解的。

我开始创建自己的实体、表和查询。第一个技巧是解决在插入时获取自动递增的 id,但示例代码涵盖了它,尽管可以改进成语。接下来要解决的问题是将所有通用代码移动到一个地方。通用代码是指基本的 CRUD 操作,基本上是所有实体的复制粘贴。所以我去创建了基地Entity

trait Entity[T <: Entity[T, ID], ID] {
  val id: Option[ID]    
  def withId(id: ID): T
}

有了这个我去创建BaseRepo应该包含所有常见代码:

abstract class BaseRepo[T <: Entity[T, ID], ID] {    
  protected val dbConfigProvider: DatabaseConfigProvider
  val dbConfig = dbConfigProvider.get[JdbcProfile]

  import dbConfig._
  import profile.api._

  type TableType <: Keyed[ID] with RelationalProfile#Table[T]

  protected val tableQuery: TableQuery[TableType]
}

WheredbConfigProvider被注入实现并允许导入正确的配置(不确定它是否需要,但示例就是这样)。是另一个用列Keyed表示 s 的特征:Tableid

trait Keyed[ID] {
  def id: Rep[ID]
}

现在一切看起来都很好。要扩展BaseRepo一个需要正确分配TableTypetableQuery并且一切都应该工作。我从以下实现开始:

case class Vehicle(override val id: Option[Long], name: String, plate: String, modelId: Long)
  extends Entity[Vehicle, Long] {
  override def withId(id: Long): Vehicle = this.copy(id = Some(id))
}

以及以下回购:

@Singleton
class VehicleRepository @Inject()(override val dbConfigProvider: DatabaseConfigProvider)
                                 (implicit ec: ExecutionContext) 
  extends BaseRepo [Vehicle, Long]{

  import dbConfig._
  import profile.api._

  type TableType = Vehicles
  val tableQuery = TableQuery[Vehicles]

  class Vehicles(tag:Tag) extends Table[Vehicle](tag:Tag, "vehicles") with Keyed[Long] {
    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def name = column[String]("name")
    def plate = column[String]("plate")
    def modelId = column[Long]("modelId")

    def * = (id.?, name,plate,modelId) <> ((Vehicle.apply _).tupled, Vehicle.unapply)
  }

一切看起来仍然很棒!现在我添加all()到 BaseRepo:

  def all() = db.run {
    tableQuery.result
  }

它有效!我可以通过简单Vehicle的注入列出我的所有实体(嗯,我要准确,但谁在乎)repo:VehicleRepositoryrepo.all()Future

接下来,我尝试使用自动递增的 id 概括插入并将其放入BaseRepo

def create(item: T, ec:ExecutionContext) = db.run{
    ((tableQuery returning tableQuery.map(_.id)) += item)
      .map(id => item.withId(id))(ec)
  }

不要介意ExecutionContext在这里明确,但无论如何,这不起作用,我得到的错误令人沮丧:

Slick does not know how to map the given types.
Possible causes: T in Table[T] does not match your * projection,
 you use an unsupported type in a Query (e.g. scala List),
 or you forgot to import a driver api into scope.
  Required level: slick.lifted.FlatShapeLevel
     Source type: slick.lifted.Rep[ID]
   Unpacked type: T
     Packed type: G
]

如果我将此方法移回VehicleRepository(替换TVehicle),一切都会像魅力一样。

经过几个小时的挖掘,我明白这tableQuery.map需要一些(implicit shape: Shape[_ <: FlatShapeLevel, F, T, G])作为隐式参数,我真的不知道它来自哪里,VehicleRepository为什么它在我的范围内不可用BaseRepo

任何关于如何解决这个问题的评论或建议,或者使用 Slick 推广 CRUD 的其他一些方法,都将受到欢迎!

我正在使用 Play-2.8 Slick-3.3.2 play-slick-5.0.0 scala-2.13.1

标签: scalaplayframeworkslickslick-3.0play-slick

解决方案


推荐阅读