0Pricing
Scala for Backend Engineering & Functional Programming · レッスン

クエリを実行する

行をケースクラスに読み込みます。

「クエリを実行する」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

sqlインターポレーター

Doobieのsql文字列インターポレーターは、クエリの中核です。SQLをパースし、埋め込まれたScalaの値を安全なバインドパラメーターに変換します。

インターポレートされた値はJDBCの?プレースホルダーになるため、SQLインジェクションが発生せず、PreparedStatementを手動で組み立てる必要もありません。

import doobie.implicits._

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

FragmentからQueryへ

sql"..."式はFragmentです。読み取りとして実行するには.query[A]を呼び出し、行の型Aを指定します。

DoobieはRead[A]型クラスを使って結果の列をAにマッピングします。プリミティブ型、タプル、ケースクラスに対応しています。

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

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

unique、option、to

Query0[A]にはさまざまなアキュムレーターがあります。.uniqueは行がちょうど1件、.optionは0件または1件であることを想定し、.to[List]はすべての行を収集します。

いずれも、後から.transactするConnectionIOを返します。

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

行をケースクラスにマッピングする

各フィールドに列マッピングがあれば、Doobieはケースクラス用のReadインスタンスを自動的に導出します。列の順序はselectリストと一致している必要があります。

これにより、定型的な行の解析コードを書かずに、豊富なドメイン型へ直接selectできます。

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

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

パラメーター化クエリ

補間されたScalaの値はすべて、そのPutインスタンスを使ってパラメーターとしてバインドされます。異なる位置を含め、いくつでも補間できます。

値は連結されずにバインドされるため、型がチェックされ、インジェクションも不可能です。

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

大きな結果セットのストリーミング

大きな結果セットには.streamを使い、fs2のStream[ConnectionIO, A]を取得します。行はサーバー側カーソルで遅延的に読み取られるため、メモリ使用量を一定に保てます。

fs2のコンビネーターと組み合わせれば、数百万行も段階的に処理できます。

import fs2.Stream

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

フェッチサイズの制御

一部のドライバーでは、デフォルトのカーソルフェッチサイズによってすべてのデータが一度に読み込まれることがあります。.streamWithChunkSize(n)はJDBCのフェッチサイズを設定し、カーソルが1回の往復でn行を取得するようにします。

これを調整することで、往復回数とメモリ使用量のバランスを取れます。

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

オプション列とNULL可能性

NULL可能な列は、行の型ではOption[A]にマッピングします。列がNULLになり得る場合はOptionとして読み取る必要があり、そうしないとDoobieはNULL値で例外をスローします。

これにより、境界部分でNULL可能性を明示的かつ型安全に扱えます。

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

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

FragmentによるSQLの構築

Fragment値は++で合成できます。パラメーターをバインドしたまま、クエリを複数の部品から組み立てられます。

fr補間子は末尾にスペースがあるFragmentを構築するため、句をつなぎ合わせるときに便利です。

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

動的なWHERE句

DoobieのFragmentsヘルパーは条件付きの句を構築します。Fragments.whereAndOptはオプションのフィルターを結合し、指定されているものだけを出力します。

これは安全な動的検索クエリを構築するための慣用的な方法です。

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)

ビルド時のクエリ検証

Doobieのcheck解析はデータベーススキーマに対してクエリを実行し、列の型と数がScalaの型と一致することを検証します。

テストで使用すると、本番環境に到達する前にスキーマのずれを検出できます。

import doobie.scalatest._

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

クイックチェック

適切なアキュムレーターを選んでください。

まとめ

sql補間子はパラメーター化されたFragmentを構築し、.query[A]はそれをRead[A]でマッピングされたQuery0[A]に変換します。

行の収集には.unique、.option、.to[List]、.streamのいずれかを選びます。動的な句にはFragmentsを使い、スキーマに対する検証にはcheckを使います。

よくある質問

「クエリを実行する」レッスンは無料ですか?

はい。「クエリを実行する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

「クエリを実行する」で何を学びますか?

行をケースクラスに読み込みます。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「クエリを実行する」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?

はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Transactorで接続する
  2. クエリを実行する
  3. 挿入と更新
  4. トランザクションを組み合わせる
← Scala for Backend Engineering & Functional Programmingに戻る