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

挿入と更新

関数型にデータを書き込みます。

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

書き込み用のUpdate0

書き込みにも同じsql補間子を使いますが、.queryの代わりに.updateを呼び出してUpdate0を取得します。

Update0で.runを実行すると、影響を受けた行数を表すConnectionIO[Int]が返されます。

import doobie.implicits._

def insert(name: String, age: Int): ConnectionIO[Int] =
  sql"insert into users (name, age) values ($name, $age)".update.run

影響を受けた行数

.runが返すIntはJDBCの更新件数です。UPDATEまたはDELETEでは、条件に一致した行数を示します。

この値で分岐すれば、たとえば一致する行がなかった更新を検出できます。

def deactivate(id: Long): ConnectionIO[Int] =
  sql"update users set active = false where id = $id".update.run

生成されたキーの取得

自動生成された主キーを取得するには、.withUniqueGeneratedKeysを使い、データベースが返す列名を指定します。

これにより、insertを実行して生成値を読み戻す処理を1回の往復で行えます。

def insertReturningId(name: String, age: Int): ConnectionIO[Long] =
  sql"insert into users (name, age) values ($name, $age)"
    .update
    .withUniqueGeneratedKeys[Long]("id")

行全体の取得

生成列やデフォルト値が設定された複数の列を、タプルまたはケースクラスに読み込んで一度に取得できます。

insert直後にタイムスタンプなどのサーバー側デフォルト値を取得する場合に便利です。

case class User(id: Long, name: String, createdAt: java.time.Instant)

def create(name: String): ConnectionIO[User] =
  sql"insert into users (name) values ($name)"
    .update
    .withUniqueGeneratedKeys[User]("id", "name", "created_at")

バッチ用のUpdateクラス

Update[A]は、何度も実行できる準備済みのパラメーター化ステートメントです。?プレースホルダーを使ってSQLを一度定義し、型Aの値を渡します。

これはSQL文字列とWrite[A]インスタンスから構築されます。

val ins: Update[(String, Int)] =
  Update[(String, Int)]("insert into users (name, age) values (?, ?)")

効率的なバッチinsert

updateManyはコレクション全体を1つのJDBCバッチで送信するため、insertを1件ずつループするよりはるかに高速です。

影響を受けた行の合計数を返します。大量の行を一度に登録する場合は、常にこれを使ってください。

val rows = List(("Ada", 36), ("Linus", 54))

val batch: ConnectionIO[Int] = ins.updateMany(rows)

キー付きバッチinsert

Updateでは、updateManyWithGeneratedKeysを使ってバッチ処理で生成されたキーをストリーミングし、insertされた各レコードに対応する行を返すこともできます。

これにより、一括登録と割り当てられた識別子の読み戻しを組み合わせられます。

import fs2.Stream

val keyed: Stream[ConnectionIO, Long] =
  ins.updateManyWithGeneratedKeys[Long]("id")(rows)

ON CONFLICTによるUpsert

データベース固有のupsertもDoobie経由で問題なく実行できます。PostgreSQLでは、通常のSQLとしてinsert ... on conflict ... do updateと記述します。

Doobieはパラメーターをバインドするだけで、upsertの意味論はデータベース側にあります。

def upsert(email: String, name: String): ConnectionIO[Int] =
  sql"""insert into accounts (email, name) values ($email, $name)
         on conflict (email) do update set name = excluded.name"""
    .update.run

Writeインスタンスとデフォルト値

パラメーターはWrite[A]型クラスでエンコードされます。プリミティブ型、タプル、ケースクラス用に導出でき、各フィールドにはPutインスタンスが必要です。

insertで列を省略すると、データベースがDEFAULTを適用します。生成されたタイムスタンプが現れるのはこの仕組みによるものです。

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

val mk = Update[NewUser](
  "insert into users (name, age) values (?, ?)"
)

Metaによるカスタム列マッピング

カスタム型を永続化するには、imap(またはtimap)を使って既存の列型にマッピングし、Meta[A]を定義します。

Metaは読み取りと書き込みの両方を提供するため、その型をクエリと更新の両方で使えます。

import doobie.util.meta.Meta

case class UserId(value: Long)

implicit val uidMeta: Meta[UserId] =
  Meta[Long].imap(UserId.apply)(_.value)

DELETEもUpdate

DELETE文も.update.runを使い、削除した行数を返します。

insert、update、deleteをすべてUpdate0として統一的に扱うことで、APIを小さく合成可能に保てます。

def purgeInactive: ConnectionIO[Int] =
  sql"delete from users where active = false".update.run

クイックチェック

最も効率的な方法を選んでください。

まとめ

書き込みでは.updateでUpdate0を生成し、.runが影響を受けた行を返し、.withUniqueGeneratedKeysが生成値を読み戻します。

大量処理ではUpdate[A].updateManyで効率的にバッチ処理します。カスタム型にはMetaインスタンスを使い、upsertはDoobieがバインドする通常のデータベースSQLとして実行します。

よくある質問

「挿入と更新」レッスンは無料ですか?

はい。「挿入と更新」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと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は初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/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に戻る