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

Выполнение запросов

Читайте строки в классы вариантов.

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

Части этого урока еще не переведены и отображаются на английском.

The sql Interpolator

Doobie's sql string interpolator is the core of querying. It parses your SQL and turns embedded Scala values into safe, bound parameters.

Interpolated values become JDBC ? placeholders, so there is no SQL injection and no manual PreparedStatement wiring.

import doobie.implicits._

val minAge = 18
val frag = sql"select name from users where age >= $minAge"

From Fragment to Query

An sql"..." expression is a Fragment. To run it as a read you call .query[A], choosing the row type A.

Doobie uses a Read[A] typeclass to map result columns onto A, supporting primitives, tuples, and case classes.

case class User(name: String, age: Int)

val q: Query0[User] =
  sql"select name, age from users".query[User]

unique, option, and to

A Query0[A] offers different accumulators. .unique expects exactly one row, .option expects zero or one, and .to[List] collects all rows.

Each returns a ConnectionIO that you later .transact.

val one: ConnectionIO[User] = q.unique
val maybe: ConnectionIO[Option[User]] = q.option
val all: ConnectionIO[List[User]] = q.to[List]

Mapping Rows to Case Classes

Doobie derives Read instances for case classes automatically when each field has a column mapping. Column order must match the select list.

This lets you select straight into rich domain types without boilerplate row parsing.

case class Account(id: Long, email: String, active: Boolean)

val accounts =
  sql"select id, email, active from accounts"
    .query[Account].to[Vector]

Parameterized Queries

Every interpolated Scala value is bound as a parameter using its Put instance. You can interpolate as many as you like, including in different positions.

Because they are bound, not concatenated, types are checked and injection is impossible.

def findByEmail(email: String): ConnectionIO[Option[Account]] =
  sql"select id, email, active from accounts where email = $email"
    .query[Account].option

Streaming Large Results

For big result sets, use .stream to get an fs2 Stream[ConnectionIO, A]. Rows are pulled lazily with a server-side cursor, keeping memory bounded.

Combine it with fs2 combinators to process millions of rows incrementally.

import fs2.Stream

val s: Stream[ConnectionIO, Account] =
  sql"select id, email, active from accounts"
    .query[Account].stream

Controlling Fetch Size

The default cursor fetch size may load everything at once on some drivers. .streamWithChunkSize(n) sets the JDBC fetch size so the cursor fetches n rows per round trip.

Tuning this balances round trips against memory.

val s =
  sql"select id, email, active from accounts"
    .query[Account]
    .streamWithChunkSize(512)

Optional Columns and Nullability

Nullable columns map to Option[A] in your row type. If a column can be NULL you must read it as an Option, or Doobie throws on a null value.

This makes nullability explicit and type-safe at the boundary.

case class Person(id: Long, nickname: Option[String])

val ps =
  sql"select id, nickname from people".query[Person].to[List]

Building SQL with Fragments

Fragment values compose with ++, letting you assemble queries from pieces while keeping parameters bound.

The fr interpolator builds a fragment with a trailing space, which is handy for stitching clauses together.

val base = fr"select id, email, active from accounts"
val where = fr"where active = $${true}"
val q = (base ++ where).query[Account]

Dynamic WHERE Clauses

Doobie's Fragments helpers build conditional clauses. Fragments.whereAndOpt combines optional filters, emitting only the ones that are present.

This is the idiomatic way to build safe dynamic search queries.

import doobie.util.fragments._

val emailOpt: Option[String] = Some("a@b.com")
val filter = whereAndOpt(emailOpt.map(e => fr"email = $e"))
val q = (fr"select id, email, active from accounts" ++ filter)

Checking Queries at Build Time

Doobie's check analysis runs a query against the database schema to verify column types and counts match your Scala types.

Used in tests, it catches schema drift before it reaches production.

import doobie.scalatest._

// inside a spec mixing in IOChecker
check(sql"select id, email, active from accounts".query[Account])

Quick Check

Choose the right accumulator.

Recap

The sql interpolator builds parameterized Fragments; .query[A] turns them into a Query0[A] mapped via Read[A].

Pick .unique, .option, .to[List], or .stream to accumulate rows. Use Fragments for dynamic clauses and check to validate against the schema.

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

Урок «Выполнение запросов» бесплатный?

Да — полный текст урока «Выполнение запросов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 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 структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Выполнение запросов»?

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

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

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

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

  1. Подключение через трансактор
  2. Выполнение запросов
  3. Вставка и обновление
  4. Композиция транзакций
← Назад к Scala for Backend Engineering & Functional Programming