平滑的通用和驱动程序不可知论者 [英] Slick generic AND driver agnostic

查看:189
本文介绍了平滑的通用和驱动程序不可知论者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本上我想要实现的是以下的组合:

Slick 3.0.0 database agnostism

Slick 3可重用通用存储库

我尝试了很多,实际上,但我无法完全解决这个问题。 / p>

 抽象类BaseModel [T<:slick.lifted.AbstractTable [_]](query:TableQuery [T],val driver :JdbcProfile,val dbTableName:String)
{
lazy val all:TableQuery [T] = TableQuery [T]
import driver.api._

def createTable = all.schema.create
def dropTable = all.schema.create

抽象类BaseTable [B](val tag:Tag)extends Table [B](tag,dbTableName)
{
def id = column [Long](id,O.PrimaryKey,O.AutoInc)
}
}
pre>

现在我们遇到问题了已经:

def createTable = all.schema.create dropTable一样。 .. - > schema 无法在这里解析,尽管我之前导入了驱动程序。



<但是,当我继承这个时,会遇到一个更大的问题:

这是代码

  class NodeModel(driver:JdbcProfile,dbTableName:String)extends BaseModel [NodeTable](TableQuery [NodeTable],driver,dbTableName){
$ b $ val dbDriver = driver
import dbDriver.api._

类NodeTable(tag:Tag)扩展BaseTable [Node](tag)
{
覆盖def * = id。? <> (Node,Node.unapply)
}

// lazy val all:TableQuery [NodeTable] = TableQuery [NodeTable]
def createTable:DBIO [Unit] = all.schema .create
def dropTable:DBIO [Unit] = all.schema.drop
def insert(node:Node)= all + = node
}

这不会明显地编译,因为我不能将 NodeTable 作为 T ,但给了我想要实现的想法。



你有什么想法如何解决这个问题吗?我还尝试使用伴随对象,将 BaseTable 移出 BaseModel 并尝试加载 simpleDriver ...但它看起来像是在最近的版本中从Slick中移除了功能:(


解决方案

数据库不可知和代码是高度可重用的

我使用 code>和 Playframework ,这就是我如何实现数据库不可知和通用存储库。



请注意,这项工作受到Active Slick的启发



像这样的基本crud操作定义在我的 case class 上,我应该能够做到 count update delete create 。我只想编写curd代码



<$ p $以下是演示这一点的片段。

  Dog(some_dog)。save()
Dog(some_dog)。insert()
Dog(some_dog,Some(1))。delete()

CrudActions .scala

  import slick.backend.DatabaseConfig 
导入slick.driver.JdbcProfile

import scala.concurrent.ExecutionContext


trait CrudActions {
val dbConfig:DatabaseConfig [JdbcProfile]
import dbConfig.driver.api._

类型

def count:DBIO [Int]

def save(model:Model)(隐式ec:ExecutionContext):DBIO [Model]

def update(模型:模型)(隐式ec:ExecutionContext):DBIO [模型]

def delete ]
$ b def fetchAll(fetchSize:Int = 100)(隐式ec:ExecutionContext):StreamingDBIO [Seq [Model],Model]
}

现在让我们将 Entity 放入图片中。请注意,实体不过是我们的案例类

实体 case class ,我们在其上进行粗暴操作。为了查找我们的实体,我们还有 Id Id 对于查找和操作数据库中的实体或记录非常重要。还有 Id 实体的唯一身份



EntityActionsLike.scala p>

 导入slick.backend.DatabaseConfig 
导入slick.driver.JdbcProfile

导入scala.concurrent .ExecutionContext

trait EntityActionsLike extends CrudActions {
val dbConfig:DatabaseConfig [JdbcProfile]
import dbConfig.driver.api._

类型实体

类型Id

类型Model = Entity

def insert(entity:Entity)(隐式ec:ExecutionContext):DBIO [Id]

def deleteById(id:Id)(隐式ec:ExecutionContext):DBIO [Int]

def findById(id:Id)(隐式ec:ExecutionContext):DBIO [实体]
$ b $ def findOptionById(id:Id)(implicit ec:ExecutionContext):DBIO [Option [Entity]]
}

现在让我们实现这些方法。为了执行操作,我们需要 Table TableQuery 。可以说我们有 tableQuery 。特性的好处是我们可以声明一个合约,并将实现细节留给子类或子类型。



EntityActions.scala p>

 导入slick.ast.BaseTypedType 
导入slick.backend.DatabaseConfig
导入slick.driver.JdbcProfile

import scala.concurrent.ExecutionContext

trait EntityActions extends EntityActionsLike {
val dbConfig:DatabaseConfig [JdbcProfile]
import dbConfig.driver.api._
$ b类型EntityTable<:Table [Entity]

def tableQuery:TableQuery [EntityTable]

def $ id(table:EntityTable):Rep [Id ]

def modelIdContract:ModelIdContract [实体,Id]

重写def count:DBIO [Int] = tableQuery.size.result

override def insert(entity:Entity)(implicit ec:ExecutionContext):DBIO [Id] = {
tableQuery.returning(tableQuery.map($ id(_)))+ = entity
}

覆盖def deleteById(id:Id)(隐式ec:ExecutionContext):DBIO [Int] = {
filterById(id).delete
}

覆盖def findById(id:Id)(隐式ec:ExecutionContext):DBIO [实体] = {
filterById(id).result.head
}

覆盖def findOptionById(id:Id)(隐式ec:ExecutionContext):DBIO [Option [Entity] ]] = {
filterById(id).result.headOption
}

重写def save(model:Entity)(implicit ec:ExecutionContext):DBIO [Entity] = {
insert(model).flatMap {id =>
filterById(id).result.head
} .transactionally
}

重写def update(model:Entity)(隐式ec:ExecutionContext):DBIO [Entity ] = {
filterById(modelIdContract.get(model))。update(model).map {_ => (模型:实体)(隐式ec:ExecutionContext):DBIO [Int] = {
filterById(modelIdContract.get(model) ).delete
}

覆盖def fetchAll(fetchSize:Int)(隐式ec:ExecutionContext):StreamingDBIO [Seq [Entity],Entity] = {
tableQuery.result。 transactionally.withStatementParameters(fetchSize = fetchSize)
}

def filterById(id:Id)= tableQuery.filter($ id(_)=== id)

def baseTypedType:BaseTypedType [Id]

保护隐式lazy val btt:BaseTypedType [Id] = baseTypedType

}

ActiveRecord.scala

  import slick.dbio.DBIO 

import scala.concurrent.ExecutionContext


抽象类ActiveRecord [R<:CrudActions](val repo:R){
def model:repo.Model
def save()(隐式ec:ExecutionContext):DBIO [repo.Model] = repo.save(模型)
def upd ate()(隐式ec:ExecutionContext):DBIO [repo.Model] = repo.update(model)
def delete()(隐式ec:ExecutionContext):DBIO [Int] = repo.delete(model)

ModelContract.scala

  case class ModelIdContract [A,B](get:A => B,集合:(A,B)=> A)

使用方法

Sample.scala

  import com.google.inject。{Inject, Singleton} 
import play.api.db.slick.DatabaseConfigProvider
import slick.ast.BaseTypedType
import slick.backend.DatabaseConfig
import slick.driver.JdbcProfile
(ActiveRecord,EntityActions,ModelIdContract)

case class Dog(name:String,id:Option [Long] = None)

@Singleton
class DogActiveRecord @Inject()(databaseConfigProvider:DatabaseConfigProvider)扩展EntityActions {

覆盖val dbConfig:DatabaseConfig [JdbcProfile] = databaseConfigProvider.get [JdbcProfile]

导入dbConfig.driver.api ._

覆盖def tableQuery = TableQuery(new Dogs(_))

覆盖def $ id(表:Dogs):Rep [Id] = table.id

覆盖def modelIdContract:ModelIdContract [Dog,Id] = ModelIdContract(dog => dog.id.get,(dog,id)=& GT; dog.copy(id = Some(id)))

覆盖def baseTypedType:BaseTypedType [Id] =隐式[BaseTypedType [Id]]

覆盖类型实体= Dog
覆盖类型Id = Long
覆盖类型EntityTable =狗

class Dogs(tag:Tag)扩展表[Dog](tag,DogsTable){
def name = column [String](name)
def id = column [Long](id,O.PrimaryKey)
def * =(name,id。?)< (Dog.tupled,Dog.unapply)
}

隐式类ActiveRecordImplicit(val模型:实体)扩展ActiveRecord(this)

import scala.concurrent.ExecutionContext .Implicits.global

result result = Dog(some_dog)。save()
$ b $ val res2 = Dog(some_other_dog,Some(1))。delete )
$ b $ val res3 = Dog(some_crazy_dog,Some(1)).update()
}

现在我们可以直接对 Dog 进行操作,就像这样



<$ p $ (some_dog)。save()

这隐含了我们的魔力

  implicit class ActiveRecordImplicit(val model:Entity)extends ActiveRecord(this)

您还可以添加方案创建并在EntityActions中放置逻辑

  tableQuery.schema.create 
table.schema.drop


Basically what I want to achieve is a combination of:

Slick 3.0.0 database agnostism and Slick 3 reusable generic repository

I tried a lot, actually, but I can't get this to work at all.

abstract class BaseModel[T <: slick.lifted.AbstractTable[_]](query: TableQuery[T], val driver: JdbcProfile, val dbTableName: String)
{
  lazy val all: TableQuery[T] = TableQuery[T]
  import driver.api._

  def createTable = all.schema.create
  def dropTable = all.schema.create

  abstract class BaseTable[B](val tag: Tag) extends Table[B](tag, dbTableName)
  {
    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  }
}

Now here we have a problem already:

def createTable = all.schema.create and the same with dropTable... -> schema cannot be resolved here, although I import the driver before.

But an even bigger problem comes in when I subclass this:

Here is the code

class NodeModel(driver: JdbcProfile, dbTableName: String) extends BaseModel[NodeTable](TableQuery[NodeTable], driver, dbTableName) {

  val dbDriver = driver
  import dbDriver.api._

  class NodeTable(tag: Tag) extends BaseTable[Node](tag)
  {
    override def * = id.? <> (Node, Node.unapply)
  }

  //lazy val all: TableQuery[NodeTable] = TableQuery[NodeTable]
  def createTable: DBIO[Unit] = all.schema.create
  def dropTable: DBIO[Unit] = all.schema.drop 
  def insert(node: Node) = all += node
}

This won't compile obviously because I cannot pass NodeTable as T, but gives an idea of what I want to achieve.

Do you have any idea how to solve this? I also tried with companion objects, moving the BaseTable out of the BaseModel and trying to load a simpleDriver... but it looks like that functionality was removed from Slick in a recent version :(

解决方案

Database agnostic and Code is highly reusable

I am using Slick with Playframework and this is how I achieved database agnostic and generic repository.

Note that this work is inspired from Active Slick

I want to have basic crud operations like this to be defined on my case class. I should be able to do count, update, delete and create. I want to write the curd code just once and reuse it for ever.

Here is the snippet which demonstrates this.

case class Dog(name: String, id: Option[Long] = None)
Dog("some_dog").save()
Dog("some_dog").insert()
Dog("some_dog", Some(1)).delete()

CrudActions.scala

import slick.backend.DatabaseConfig
import slick.driver.JdbcProfile

import scala.concurrent.ExecutionContext


trait CrudActions {
  val dbConfig: DatabaseConfig[JdbcProfile]
  import dbConfig.driver.api._

  type Model

  def count: DBIO[Int]

  def save(model: Model)(implicit ec: ExecutionContext): DBIO[Model]

  def update(model: Model)(implicit ec: ExecutionContext): DBIO[Model]

  def delete(model: Model)(implicit ec: ExecutionContext): DBIO[Int]

  def fetchAll(fetchSize: Int = 100)(implicit ec: ExecutionContext): StreamingDBIO[Seq[Model], Model]
}

Now lets get our Entity into picture. Note that Entity is nothing but our case class

Entity is case class on which we do crud operations. For locating our entity lets also have Id in place. Id is important for locating and operating an entity or record in the database. Also Id uniquely identities for entity

EntityActionsLike.scala

import slick.backend.DatabaseConfig
import slick.driver.JdbcProfile

import scala.concurrent.ExecutionContext

trait EntityActionsLike extends CrudActions {
  val dbConfig: DatabaseConfig[JdbcProfile]
  import dbConfig.driver.api._

  type Entity

  type Id

  type Model = Entity

  def insert(entity: Entity)(implicit ec: ExecutionContext): DBIO[Id]

  def deleteById(id: Id)(implicit ec: ExecutionContext): DBIO[Int]

  def findById(id: Id)(implicit ec: ExecutionContext): DBIO[Entity]

  def findOptionById(id: Id)(implicit ec: ExecutionContext): DBIO[Option[Entity]]
}

Now lets implement these methods. For doing operations we need Table and TableQuery. Lets say we have table and tableQuery. The good about traits is we can declare a contract and leave the implementation details to subclasses or subtypes

EntityActions.scala

import slick.ast.BaseTypedType
import slick.backend.DatabaseConfig
import slick.driver.JdbcProfile

import scala.concurrent.ExecutionContext

trait EntityActions extends EntityActionsLike {
  val dbConfig: DatabaseConfig[JdbcProfile]
  import dbConfig.driver.api._

  type EntityTable <: Table[Entity]

  def tableQuery: TableQuery[EntityTable]

  def $id(table: EntityTable): Rep[Id]

  def modelIdContract: ModelIdContract[Entity,Id]

  override def count: DBIO[Int] = tableQuery.size.result

  override def insert(entity: Entity)(implicit ec: ExecutionContext): DBIO[Id] = {
    tableQuery.returning(tableQuery.map($id(_))) += entity
  }

  override def deleteById(id: Id)(implicit ec: ExecutionContext): DBIO[Int] = {
    filterById(id).delete
  }

  override def findById(id: Id)(implicit ec: ExecutionContext): DBIO[Entity] = {
    filterById(id).result.head
  }

  override def findOptionById(id: Id)(implicit ec: ExecutionContext): DBIO[Option[Entity]] = {
    filterById(id).result.headOption
  }

  override def save(model: Entity)(implicit ec: ExecutionContext): DBIO[Entity] = {
    insert(model).flatMap { id =>
      filterById(id).result.head
    }.transactionally
  }

  override def update(model: Entity)(implicit ec: ExecutionContext): DBIO[Entity] = {
    filterById(modelIdContract.get(model)).update(model).map { _ => model }.transactionally
  }

  override def delete(model: Entity)(implicit ec: ExecutionContext): DBIO[Int] = {
    filterById(modelIdContract.get(model)).delete
  }

  override def fetchAll(fetchSize: Int)(implicit ec: ExecutionContext): StreamingDBIO[Seq[Entity], Entity] = {
    tableQuery.result.transactionally.withStatementParameters(fetchSize = fetchSize)
  }

  def filterById(id: Id) = tableQuery.filter($id(_) === id)

  def baseTypedType: BaseTypedType[Id]

  protected implicit lazy val btt: BaseTypedType[Id] = baseTypedType

}

ActiveRecord.scala

import slick.dbio.DBIO

import scala.concurrent.ExecutionContext


abstract class ActiveRecord[R <: CrudActions](val repo: R) {
  def model: repo.Model
  def save()(implicit ec: ExecutionContext): DBIO[repo.Model] = repo.save(model)
  def update()(implicit ec: ExecutionContext): DBIO[repo.Model] = repo.update(model)
  def delete()(implicit ec: ExecutionContext): DBIO[Int] = repo.delete(model)
}

ModelContract.scala

case class ModelIdContract[A, B](get: A => B, set: (A, B) => A)

How to Use

Sample.scala

import com.google.inject.{Inject, Singleton}
import play.api.db.slick.DatabaseConfigProvider
import slick.ast.BaseTypedType
import slick.backend.DatabaseConfig
import slick.driver.JdbcProfile
import slick.{ActiveRecord, EntityActions, ModelIdContract}

case class Dog(name: String, id: Option[Long] = None)

@Singleton
class DogActiveRecord @Inject() (databaseConfigProvider: DatabaseConfigProvider) extends EntityActions {

  override val dbConfig: DatabaseConfig[JdbcProfile] = databaseConfigProvider.get[JdbcProfile]

  import dbConfig.driver.api._

  override def tableQuery = TableQuery(new Dogs(_))

  override def $id(table: Dogs): Rep[Id] = table.id

  override def modelIdContract: ModelIdContract[Dog, Id] = ModelIdContract(dog => dog.id.get, (dog, id) => dog.copy(id = Some(id)))

  override def baseTypedType: BaseTypedType[Id] = implicitly[BaseTypedType[Id]]

  override type Entity = Dog
  override type Id = Long
  override type EntityTable = Dogs

  class Dogs(tag: Tag) extends Table[Dog](tag, "DogsTable") {
    def name = column[String]("name")
    def id = column[Long]("id", O.PrimaryKey)
    def * = (name, id.?) <> (Dog.tupled, Dog.unapply)
  }

  implicit class ActiveRecordImplicit(val model: Entity) extends ActiveRecord(this)

  import scala.concurrent.ExecutionContext.Implicits.global

  val result = Dog("some_dog").save()

  val res2 = Dog("some_other_dog", Some(1)).delete()

  val res3 = Dog("some_crazy_dog", Some(1)).update()
}

Now we can do operations on Dog directly like this

Dog("some_dog").save()

This implicit does the magic for us

implicit class ActiveRecordImplicit(val model: Entity) extends ActiveRecord(this)

You can also add scheme creation and dropping logic in EntityActions

tableQuery.schema.create
table.schema.drop

这篇关于平滑的通用和驱动程序不可知论者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆