在数据访问层重用 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 特征,然后能够将它们用于您想要的任何特定于数据库的驱动程序实现。
我正在尝试使用 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 特征,然后能够将它们用于您想要的任何特定于数据库的驱动程序实现。