在数据访问层重用 Slick 的 DB 驱动程序代码

Reusing Slick's DB driver code in data access layer

我正在尝试使用 Slick 3.0 进行数据访问。在查阅了各种 github 示例之后,我得出了以下设计。

注入DataSource和Driver实例的单例Slick对象

class Slick(dataSource: DataSource, val driver: JdbcDriver)  {

  val db = driver.api.Database.forDataSource(dataSource)     

}

每个 DB table 定义映射的特征

特征混合在构建查询的上层

trait RecipeTable {

  protected val slick: Slick

  // the ugly import that have to be added when Slick API is used
  import slick.driver.api._

  type RecipeRow = (Option[Long], String)

  class RecipeTable(tag: Tag) extends Table[RecipeRow](tag, "recipe") {

    def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
    def name = column[String]("name")

    def * = (id, name)    
  }

  protected val recipes = TableQuery[RecipeTable]    
}

现在有一个明显的缺点,即在每个 *Table 特征中以及在混合其中的每个地方我都需要复制 import slick.driver.api._ 以便将所有 Slick 的东西都包含在范围内。

这是我想避免的事情。理想情况下,导入将只定义一次并在下游组件中重复使用。

您能否建议解决此类重复的设计?

我的灵感主要来自 this 示例,但是导入也在那里重复。

"ugly" import 实际上是 slick 设计的一个好东西。但是你的流畅使用方式可以改进如下,

创建将提供 JdbcDriver

的特征
package demo.slick.dbl

trait SlickDriverComponent {
  val driver: JdbcDriver
}

trait SlickDBComponent extends SlickDriverComponent {
  val db: driver.api.Database
}

现在将您的 DAO 特征定义为依赖于该特征的特征,

package demo.slick.dao

import demo.slick.dbl.SlickDBComponent

trait RecipeDAO { self: SlickDBComponent =>

  import driver.api._

  type RecipeRow = (Option[Long], String)

  class RecipeTable(tag: Tag) extends Table[RecipeRow](tag, "recipe") {

    def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
    def name = column[String]("name")

    def * = (id, name)    
  }

  val recipes = TableQuery[RecipeTable]

  def get5Future = db.run(recipes.take(5).result)

}

当涉及到实际连接数据库和做事时,

package demo.slick.dbl

trait MySqlDriverProvider extends SlickDriverComponent {          
  val driver = slick.driver.MySQLDriver
}

object MySqlDBConnection extends MySqlDriverProvider {
  val connection = driver.api.Database.forConfig("mysql")
}

trait MySqlDBProvider extends SlickDBComponent {          
  val driver = slick.driver.MySQLDriver
  val db: Database = MySqlDBConnection.connection
}

trait PostgresDriverProvider extends SlickDriverComponent {          
  val driver = slick.driver.PostgresDriver
}

object PostgresDBConnection extends PostgresDriverProvider {
  val connection = driver.api.Database.forConfig("postgres")
}

trait PostgresDBProvider extends SlickDBComponent {
  val driver = slick.driver.PostgresDriver
  val db: Database = PostgresDBConnection.connection
}

现在终于可以如下定义你的 DAO 对象了,

package demo.slick.dao

import demo.slick.dbl.MySqlDBProvider

object MySqlRecipeDAO extends RecipeDAO with MySqlDBProvider

object PostgresRecipeDAO extends RecipeDAO with PostgresDBProvider

现在,您可以按如下方式使用它们,

pakcage demo.slick

import scala.util.{Failure, Success, Try}
import scala.concurrent.ExecutionContext.Implicits.global

import demo.slick.RecipeDAO

object App extends Application {
  val recipesFuture = MysqlRecipeDAO.get5Future

  recipesFuture.onComplete({
    case Success(seq) => println("Success :: found :: " + seq)
    case Failure(ex) => println("Failure :: failed :: " + ex.getMessage)
  })
}

现在...众所周知,不同的数据库具有不同的功能集,因此您可以使用的 "things" 将取决于所使用的驱动程序。

因此每次都需要进行丑陋的导入,这样您就可以编写一次 DAO 特征,然后能够将它们用于您想要的任何特定于数据库的驱动程序实现。