Подключение через трансактор
Настройте доступ к базе данных.
«Подключение через трансактор» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Что такое трансактор?
Doobie — это чистый функциональный слой JDBC для Scala. Transactor[F] связывает чистые программы Doobie с реальным соединением с базой данных.
Трансактор умеет получать JDBC-соединение Connection, выполнять вашу программу внутри транзакции и безопасно освобождать ресурсы.
Он параметризуется типом эффекта F[_], например cats.effect.IO.
import doobie._
import cats.effect.IO
val xa: Transactor[IO] = ???Программа ConnectionIO
Запросы Doobie имеют тип ConnectionIO[A]. Они описывают вычисление, которому требуется JDBC-соединение, но пока не запускаются.
Вы строите эти программы чисто, а затем передаёте их трансактору для выполнения. До запуска программы база данных никак не затрагивается.
import doobie.implicits._
val program: ConnectionIO[Int] =
sql"select 42".query[Int].uniqueDriverManagerTransactor
Самый простой трансактор — Transactor.fromDriverManager. Он открывает новое соединение для каждой транзакции с помощью DriverManager JDBC.
Ему нужны имя класса драйвера, URL JDBC, имя пользователя и пароль. Он подходит для тестов и сценариев, но не использует пул соединений.
val xa = Transactor.fromDriverManager[IO](
driver = "org.postgresql.Driver",
url = "jdbc:postgresql://localhost:5432/app",
user = "postgres",
password = "secret",
logHandler = None
)Запуск программы
Чтобы выполнить ConnectionIO, вызовите .transact(xa). В результате получится F[A], в данном случае IO[A].
Трансактор оборачивает программу в транзакцию: при успехе фиксирует изменения, при ошибке откатывает их, а соединение освобождает в любом случае.
import doobie.implicits._
val result: IO[Int] =
sql"select 42".query[Int].unique.transact(xa)Пул соединений с HikariCP
В рабочем окружении нужен пул соединений. Doobie предоставляет HikariTransactor, использующий HikariCP.
Он создаётся как Resource, поэтому пул корректно завершается. Также необходимо передать контекст выполнения для блокирующих операций JDBC.
import doobie.hikari.HikariTransactor
import cats.effect.IO
val xaRes: Resource[IO, HikariTransactor[IO]] =
HikariTransactor.newHikariTransactor[IO](
"org.postgresql.Driver",
"jdbc:postgresql://localhost/app",
"postgres", "secret",
connectEC
)Зачем нужен Resource?
Пуловый трансактор владеет долгоживущим состоянием: открытыми соединениями и фоновыми потоками. Resource[F, A] гарантирует, что получение и освобождение ресурса выполняются парно даже при ошибках или отмене.
Обычно трансактор создают один раз при запуске и используют на протяжении всего времени работы приложения.
xaRes.use { xa =>
program.transact(xa)
}Пул connectEC
HikariCP нужен ExecutionContext, чтобы ожидать получения соединений из пула. Doobie предоставляет для этого ExecutionContexts.fixedThreadPool, который сам является Resource.
Отделение этого пула от пула вычислений не позволяет блокирующему получению соединений лишать ресурсов задачи, нагружающие CPU.
import doobie.util.ExecutionContexts
val poolRes =
for {
ec <- ExecutionContexts.fixedThreadPool[IO](8)
xa <- HikariTransactor.newHikariTransactor[IO](
"org.postgresql.Driver", url, user, pass, ec)
} yield xaНастройка существующего DataSource
Если у вас уже есть настроенный javax.sql.DataSource, например оптимизированный экземпляр Hikari, напрямую оберните его с помощью Transactor.fromDataSource.
Это распространённый подход, когда фреймворк управляет пулом, а от Doobie требуется только его использовать.
val xa = Transactor.fromDataSource[IO](
dataSource = myDataSource,
connectEC = connectEC
)Пробная проверка соединения
Простая проверка работоспособности — выполнить через трансактор запрос select 1. Если он возвращает результат, драйвер, URL и учётные данные работают.
Это хороший первый шаг перед подключением настоящих запросов.
val check: IO[Int] =
sql"select 1".query[Int].unique.transact(xa)
// check.unsafeRunSync() == 1Внутреннее устройство трансактора: стратегия
Трансактор строится из интерпретаторов и Strategy. Стратегия управляет действиями вокруг каждой транзакции: before, after, oops (при ошибке) и always.
Стратегия по умолчанию отключает автоматическую фиксацию, фиксирует изменения после успеха, выполняет откат при ошибке и всегда закрывает соединение.
import doobie.util.transactor.Strategy
val noCommit = xa.copy(
strategy0 = Strategy.default.copy(after = doobie.free.connection.unit)
)Запись событий и наблюдаемость
Doobie может записывать каждый оператор, его аргументы и время выполнения. В современных версиях к каждому запросу подключается LogHandler[F], а не один обработчик на трансактор.
Это особенно полезно для поиска медленных запросов и проверки привязки параметров в рабочем окружении.
import doobie.util.log._
val handler: LogHandler[IO] = (ev: LogEvent) =>
IO.println(ev.sql)Быстрая проверка
Проверьте, насколько хорошо вы понимаете трансакторы.
Итоги
Transactor[F] связывает чистые программы ConnectionIO с реальной базой данных. fromDriverManager подходит для тестов, а HikariTransactor и fromDataSource — для рабочего окружения с использованием пула.
Создавайте пуловые трансакторы как Resource, запускайте программы с помощью .transact(xa), а обработку фиксации, отката и очистки поручайте Strategy по умолчанию.
Часто задаваемые вопросы
Урок «Подключение через трансактор» бесплатный?
Да — полный текст урока «Подключение через трансактор» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Чему я научусь в уроке «Подключение через трансактор»?
Настройте доступ к базе данных. Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?
Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Подключение через трансактор»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?
Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Подключение через трансактор
- Выполнение запросов
- Вставка и обновление
- Композиция транзакций