doobieの紹介–Scala用のJDBCレイヤー
1. 序章
データベースアクセスは、多くのソフトウェアシステムの重要な部分です。 JVMはJDBCを提供します。これを使用して、その対話を容易にすることができます。 この記事では、 doobie –Scala用の純粋に機能的なJDBCレイヤーについて見ていきます。
doobieはCatsおよびCatsEffect ライブラリを使用するため、doobieを使用する前にそれらにある程度精通していることが重要です。
2. 設定
プロジェクトにdoobieを追加するには、sbtを使用します。
libraryDependencies += "org.tpolecat" %% "doobie-core" % "1.0.0-RC1"
これは、doobieを使用する必要がある唯一の依存関係ですが、データベースとしてPostgreSQLを使用するため、PostgreSQL拡張機能も追加しましょう。
libraryDependencies += "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC1"
この紹介では、 worldデータベースがロードされたPostgreSQLを使用します。具体的には、cityテーブルを使用します。
create table city (
id integer not null,
name text not null,
country_code char(3) not null,
district text not null,
population integer not null
);
3. doobieを使用する
プロジェクトにdoobieが追加されたので、Transactionorを使用してデータベース接続を作成する方法を見てみましょう。
3.1. データベースへの接続
Transactionor を使用してデータベースに接続することを説明しましたが、これは完全に正確ではありません。 結果のタイプTransactorは、データベースに接続する方法を知っていますが、後でこの接続をクリーンアップする方法、そして最も重要なこととして、IOConnectionを取得する方法も知っています。そしてその結果を私たちが選んだ効果に変換します–この場合、IO。
これにより、定義を実行から明確に分離できます。
val transactor: Transactor[IO] = Transactor.fromDriverManager[IO](
"org.postgresql.Driver",
"jdbc:postgresql://localhost:5432/world-db",
"world",
"world123"
)
val operations: ConnectionIO[Unit] = ???
operations.transact(transactor) // IO[Unit]
操作内のすべてのアクションは、単一のトランザクションで実行されます。
Transactor を介してデータベース操作を直接実行することは確かに機能しますが、毎回新しい接続を作成するため、効率的ではありません。 したがって、接続プールを使用でき、使用する必要があります。
3.2. クエリ
データベースから都市の名前を選択するとします。 クエリは次のように記述できます。
sql"select name from city limit 5"
.query[String]
.to[List]
このコードを段階的に見ていきましょう。 まず、sql補間器を使用してSQLステートメントを定義します。
sql"select name from city" // Fragment
その結果、 Fragment タイプになります。これについては、後のセクションで説明します。 次に、このSQLを特定のタイプのクエリとして実行することをdoobieに伝えることができます。
.query[String] // Query0[String]
name は文字列であるため、Stringを使用しています。 タプルまたは一致するケースクラスを型として提供することもできます。重要なのは、パラメーターの数がSQL自体で指定したものと一致する必要があることです。
最後に、コレクションタイプを指定して、クエリ定義を完成させることができます。
.to[List] // ConnectionIO[List[String]]
これにより、 ConnectionIO モナドが生成され、これを使用して複数の操作を連鎖させることができます。 クエリが単一の行を返すことが確実な場合は、 .unique メソッドを使用することもできます。これにより、 ConnectionIO [String] が生成され、次の場合にも例外がスローされます。複数の行が返されます。
データベースからデータを取得する方法がわかったので、いくつかのデータの挿入と更新について考えることができます。
3.3. データの挿入、更新、および削除
データベースに新しい都市を挿入しましょう。
sql"insert into city (id, name, country_code, district, population) values (5000, 'Baeldung', 'NLD', 'Baeldungland', 1337)"
.update
.run
それでは、 select 操作で行ったように、このコードを分析してみましょう。 まず、SQLステートメントを使用したsql補間器があります。
sql"insert into city (id, name, country_code, district, population) values (5000, 'Baeldung', 'NLD', 'Baeldungland', 1337)"
データをStringに直接挿入しましたが、補間することもできます。
val id = 5000
sql"insert into city (id, name, country_code, district, population) values ($id, 'Baeldung', 'NLD', 'Baeldungland', 1337)"
次に、このSQLが.updateメソッドを使用して変更することをdoobieに通知します。
.update // Update0
そして、あとは.runメソッドを使用してこの操作を完了するだけです。
.run // ConnectionIO[Int]
モナド内のIntに注意してください。 この場合、影響を受ける行の数を示しますが、代わりに特定のデータを要求することもできます。
insertedId <- sql"insert into city (id, name, country_code, district, population) values (${baeldungCity.id}, ${baeldungCity.name}, ${baeldungCity.countryCode}, ${baeldungCity.district}, ${baeldungCity.population})"
.update
.withUniqueGeneratedKeys[Int]("id")
withUniqueGeneratedKeys を使用して、挿入された id を取得しますが、他の列を取得するためにも使用できます。 ただし、この機能が機能するには、データベースでサポートされている必要があることに注意してください。
更新と削除は、データベースの状態を変更するという意味で、挿入と非常によく似ています。 そのため、同じ方法を使用できます。
sql"update city set name = 'DungBael' where id = 5000".update.run
そしてdeleteの場合:
sql"delete from city where id = 5000".update.run
3.4. フラグメント
フラグメントはdoobieの非常に重要な概念です。 それらの有用性は、例で最もよく示されています。 オプションのパラメータoptionalCityNameParamがあるとしましょう。
val optionalCityNameParam: Option[String] = Some("%Pol%")
フラグメント補間器frを使用して、オプションのFragmentを作成するようにマップできます。
val optionalCityNameFragment: Option[Fragment] = optionalCityNameParam.map(name => fr"name like $name")
最後に、クエリ内に配置できます。
val operation = (fr"select name from city" ++ whereAndOpt(optionalCityNameFragment)).query[String].to[List]
whereAndOptメソッドの使用法に注意してください。 これは、 doobie.Fragments、にある多くのヘルパーメソッドの1つですが、この特定のメソッドはオプションのフラグメントを受け入れ、Someの場合にのみ適用します。 多くのフラグメントを作成し、さまざまな方法でそれらを接続できます。 たとえば、結果を5行に制限するSQLの一部は、フラグメントである可能性があります。
val limitFragment = fr"limit 5"
他のSQLにも追加できます。
fr"select id, name, country_code, district, population from city" ++ limitFragment
これは、でSQLスニペットを簡単に再利用でき、オプションで簡単に使用できる非常に便利な機能です。 ちなみに、frとsql補間器の唯一の違いは、 fr がフラグメントの後にスペースを追加して連結を支援し、sqlがはそうではありません。 したがって、 2つのSQLスニペットを結合する場合は常に、fr補間器を使用する必要があります。
3.5. エラー処理
一般に、doobieは、 IO と同様に、は、明示的に処理されない限り、エラーの伝播とエスケープを許可します。 これを行うには、Catの.attemptまたは.raiseErrorを使用できますが、doobieには独自の省略形がいくつか用意されています。 これらは主にSQLExceptionの処理に重点を置いており、状況に応じて使用されるため、これらについて説明することはこの紹介の範囲外です。 完全なリストは、doobieのscaladocにあります。
4. 結論
この記事では、doobieで基本的な操作を実行する方法を見てきました。 接続を形成する方法と、選択、挿入、削除、および更新する方法を学び、doobieフラグメントについて学びました。 ロギング、データストリーミング、IO以外のエフェクトの使用方法など、未踏のトピックがまだいくつかありますが、これで十分に紹介できます。
いつものように、この記事のコードはGitHubでから入手できます。