使用 Slick/Doobie 集成数据库
学习使用 Slick 或 Doobie 等热门 Scala ORM/库,将数据库集成到 Play 应用程序中。
使用 Slick/Doobie 集成数据库 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 3 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 3 节课。
为什么要集成数据库?
Web 应用程序通常需要持久化存储和检索数据。这些数据可能包括用户资料、产品信息或博客文章。
- 持久性:即使应用程序重启,数据仍然可用。
- 可扩展性:处理大量数据和许多并发用户。
- 可靠性:确保数据完整性,并支持数据恢复。
对于使用 Play Framework 构建的任何动态 Web 应用程序来说,集成数据库都至关重要。
Slick 与 Doobie:Scala 的数据库工具
Scala 提供了功能强大的数据库交互库,其中 Slick 和 Doobie 是两个常用选择。
- Slick:一个函数式关系映射(FRM)库。它让您能够以类型安全、函数式的方式操作数据库,同时隐藏 SQL 的具体细节。
- Doobie:一个纯函数式 JDBC 层。它让您能够直接控制 SQL 查询,同时与 Cats Effect 等函数式编程范式集成。
本课程将重点介绍 Slick,因为它提供了更高层次的抽象,通常能够简化与 Play Framework 的初始集成。
在 Play 中设置 Slick
首先,将 Slick 和数据库驱动程序添加到您的 build.sbt 中。为便于设置,我们将使用 H2 这一内存数据库。
然后,在 conf/application.conf 中配置数据库连接。
import play.sbt.PlayImport._
libraryDependencies ++= Seq(
jdbc,
ws,
"com.typesafe.play" %% "play-slick" % "5.0.0",
"com.typesafe.play" %% "play-slick-evolutions" % "5.0.0",
"com.h2database" % "h2" % "2.2.224"
)数据库配置
将以下代码行添加到 conf/application.conf,以设置 H2 数据库和 Slick 配置。slick.dbs.default.profile 指定数据库类型。
play.db.prototype.slick 定义数据库连接的模板。
slick.dbs.default.profile="slick.jdbc.H2Profile$"
play.db {
prototype.slick = {
db.driver="org.h2.Driver"
db.url="jdbc:h2:mem:play;DB_CLOSE_DELAY=-1"
db.username="sa"
db.password=""
}
# Apply the prototype to our default DB
default = ${play.db.prototype.slick}
}定义您的模型(Slick 表)
在 Slick 中,您可以将数据库表表示为扩展 slick.jdbc.H2Profile.api.Table 的 Scala 类。每一列都会映射到一个 Scala 类型。
我们会为每个表定义一个 TableQuery,它是执行查询的入口。
import slick.jdbc.H2Profile.api._
// Define a case class for our data model
case class User(id: Option[Long], name: String, email: String)
// Define the table mapping
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def email = column[String]("email")
// Every table needs a * projection with the default mapping
def * = (id, name, email) <> (User.tupled, User.unapply)
}
// TableQuery for accessing the Users table
val users = TableQuery[Users]代码:创建表和架构
此可运行示例展示了如何定义 User 表,然后在内存 H2 数据库中创建其架构。db.run 方法会异步执行数据库操作。
import slick.jdbc.H2Profile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
case class User(id: Option[Long], name: String, email: String)
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def email = column[String]("email")
def * = (id, name, email) <> (User.tupled, User.unapply)
}
object SlickDemo {
val users = TableQuery[Users]
def main(args: Array[String]): Unit = {
val db = Database.forURL("jdbc:h2:mem:test1;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
try {
val setup = DBIO.seq(
users.schema.create
)
val future = db.run(setup)
Await.result(future, 2.seconds)
println("Users table created successfully!")
} finally {
db.close()
}
}
}用于执行操作的 DAO 模式
数据访问对象(DAO)模式有助于封装数据库操作。它将应用程序的业务逻辑与持久化逻辑分离。
UserDAO 会包含用于插入、查询、更新和删除 User 对象的方法。
import slick.jdbc.H2Profile.api._
import scala.concurrent.Future
class UserDAO(db: Database) {
import SlickDemo.users // Assuming users TableQuery is in SlickDemo object
def insert(user: User): Future[User] = {
db.run(users returning users.map(_.id) into ((user, id) => user.copy(id = id)) += user)
}
def findAll(): Future[Seq[User]] = {
db.run(users.result)
}
def findById(id: Long): Future[Option[User]] = {
db.run(users.filter(_.id === id).result.headOption)
}
}代码:插入和查询数据
下面介绍如何使用刚才定义的 DAO 方法插入新用户,然后从数据库中查询所有用户。
import slick.jdbc.H2Profile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
case class User(id: Option[Long], name: String, email: String)
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def email = column[String]("email")
def * = (id, name, email) <> (User.tupled, User.unapply)
}
class UserDAO(db: Database) {
val users = TableQuery[Users]
def insert(user: User): Future[User] =
db.run(users returning users.map(_.id) into ((u, id) => u.copy(id = id)) += user)
def findAll(): Future[Seq[User]] =
db.run(users.result)
}
object SlickOperations {
def main(args: Array[String]): Unit = {
val db = Database.forURL("jdbc:h2:mem:test2;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
val userDAO = new UserDAO(db)
try {
Await.result(db.run(userDAO.users.schema.create), 2.seconds)
println("Table created.")
val future = for {
user1 <- userDAO.insert(User(None, "Alice", "alice@example.com"))
user2 <- userDAO.insert(User(None, "Bob", "bob@example.com"))
allUsers <- userDAO.findAll()
} yield allUsers
val result = Await.result(future, 5.seconds)
println(s"All users: $result")
} finally {
db.close()
}
}
}将 DAO 集成到 Play 中
在 Play 应用程序中,通常会通过依赖注入将 UserDAO 注入控制器。这样,控制器操作就可以与数据库交互。
您需要配置 Play 的 DI(例如 Guice),以提供 Database 和 UserDAO 的实例。
import play.api.mvc._
import play.api.libs.json._
import scala.concurrent.ExecutionContext
import javax.inject._
// Assuming User and UserDAO are defined as before
@Singleton
class UserController @Inject()(cc: ControllerComponents, userDAO: UserDAO)
(implicit ec: ExecutionContext) extends AbstractController(cc) {
def listUsers = Action.async {
userDAO.findAll().map { users =>
Ok(Json.toJson(users))
}
}
def createUser = Action.async(parse.json) {
request =>
request.body.validate[User].map { user =>
userDAO.insert(user).map(u => Created(Json.toJson(u)))
}.getOrElse(Future.successful(BadRequest("Invalid User Data")))
}
}快速检查:Slick 表
请考虑以下 Scala 样例类和部分定义的 Slick Table 映射:
case class Product(id: Option[Int], name: String, price: Double)假设已经定义了 id、name 和 price 列,哪一行代码能够正确补全 Product 表的 * 投影?
回顾:数据库集成
在本课程中,我们探索了如何使用 Slick 将数据库集成到 Play Framework 应用程序中。
- 我们了解了 Slick 作为函数式关系映射(FRM)库所发挥的作用。
- 我们介绍了如何设置 Slick 依赖项,以及如何在
application.conf中配置数据库。 - 您了解了如何定义 Scala 样例类,并使用 Slick 的
Table类将其映射到数据库表。 - 我们介绍了 DAO 模式,用于组织数据库操作,并提供了插入和查询数据的示例。
- 最后,我们简要介绍了如何将这些 DAO 操作集成到 Play 控制器中,以提供 Web API 端点。
掌握数据库集成是构建健壮、数据驱动的 Play 应用程序的关键!
常见问题解答
「使用 Slick/Doobie 集成数据库」课时是免费的吗?
是的 — 「使用 Slick/Doobie 集成数据库」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 3 节课。
「使用 Slick/Doobie 集成数据库」这节课中我会学到什么?
学习使用 Slick 或 Doobie 等热门 Scala ORM/库,将数据库集成到 Play 应用程序中。 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 3 节。
「使用 Slick/Doobie 集成数据库」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?
能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- Play Framework 基础
- 使用 Play 构建 RESTful API
- 使用 Slick/Doobie 集成数据库