Scala for Backend Engineering & Functional Programming · Урок

Интеграция с базами данных через Slick/Doobie

Научитесь интегрировать базы данных в приложение Play с помощью популярных ORM и библиотек Scala, таких как Slick и Doobie.

Урок 3 из 311 шагов

«Интеграция с базами данных через Slick/Doobie» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 3 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 3 уроков всего.

Зачем интегрировать базы данных?

Веб-приложениям часто требуется постоянно хранить и извлекать данные. Это могут быть профили пользователей, сведения о товарах или записи в блоге.

  • Сохранность: данные остаются доступными даже после перезапуска приложения.
  • Масштабируемость: возможность обрабатывать большие объёмы данных и множество одновременных пользователей.
  • Надёжность: обеспечение целостности данных и возможности их восстановления.

Интеграция базы данных важна для любого динамического веб-приложения, созданного с помощью Play Framework.

Слик и Дуби: инструменты работы с базами данных в Scala

Scala предлагает мощные библиотеки для взаимодействия с базами данных; двумя популярными вариантами являются Слик и Дуби.

  • Слик: библиотека функционального реляционного отображения (FRM). Она позволяет работать с базами данных типобезопасным и функциональным способом, абстрагируясь от SQL.
  • Дуби: полностью функциональный слой JDBC. Он предоставляет прямой контроль над SQL-запросами и при этом интегрируется с парадигмами функционального программирования, такими как Cats Effect.

В этом уроке мы сосредоточимся на Слик благодаря его более высокоуровневой абстракции, которая часто упрощает первоначальную интеграцию с Play Framework.

Настройка Слик в Play

Сначала добавьте Слик и драйвер базы данных в файл 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.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}
}

Определение модели (таблица Слик)

В Слик таблицы базы данных представляются классами Scala, которые расширяют slick.jdbc.H2Profile.api.Table. Каждый столбец сопоставляется с типом 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 в контроллер с помощью внедрения зависимостей. Это позволяет действиям контроллера взаимодействовать с базой данных.

Вам потребуется настроить DI Play (например, 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")))
  }
}

Быстрая проверка: таблица Слик

Рассмотрим следующий класс вариантов Scala и частично определённое сопоставление таблицы Слик Table:

case class Product(id: Option[Int], name: String, price: Double)

Какая строка корректно завершает проекцию * для таблицы Product, если столбцы id, name и price уже определены?

Повторение: интеграция базы данных

В этом уроке мы изучили интеграцию баз данных в приложения Play Framework с помощью Слик.

  • Мы узнали о роли Слик как библиотеки функционального реляционного отображения (FRM).
  • Мы рассмотрели добавление зависимостей Слик и настройку базы данных в application.conf.
  • Вы увидели, как определять классы вариантов Scala и сопоставлять их с таблицами базы данных с помощью класса Слик Table.
  • Мы познакомились с шаблоном DAO для организации операций с базой данных и рассмотрели примеры добавления и поиска данных.
  • Наконец, мы затронули интеграцию этих операций DAO с контроллерами Play для конечных точек веб-API.

Освоение интеграции баз данных — ключ к созданию надёжных приложений Play, управляемых данными!

Можно начать бесплатно

Изучай Scala с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
39
Уроки
143

Часто задаваемые вопросы

Урок «Интеграция с базами данных через Slick/Doobie» бесплатный?

Да — полный текст урока «Интеграция с базами данных через Slick/Doobie» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 3 уроков всего.

Чему я научусь в уроке «Интеграция с базами данных через Slick/Doobie»?

Научитесь интегрировать базы данных в приложение Play с помощью популярных ORM и библиотек Scala, таких как Slick и Doobie. Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?

Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 3.

Сколько времени занимает урок «Интеграция с базами данных через Slick/Doobie»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?

Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Основы Play Framework
  2. Создание RESTful API с Play
  3. Интеграция с базами данных через Slick/Doobie
← Назад к Scala for Backend Engineering & Functional Programming