GroovyによるJDBC

1前書き

この記事では、慣用的なGroovyを使用して、 JDBC を使用してリレーショナル・データベースを照会する方法を調べます。

JDBCは比較的低レベルですが、JVM上のほとんどのORMおよびその他の高レベルデータアクセスライブラリの基盤です。もちろん、GroovyでJDBCを直接使用することもできます。しかし、それはかなり面倒なAPIを持っています。

幸いなことに、Groovy標準ライブラリはJDBCをベースにして、クリーンでシンプル、しかも強力なインターフェースを提供します。それで、私たちはGroovy SQLモジュールを探っています。

私たちがリンクしているSpringのようなフレームワークを考慮せずに、普通のGroovyでJDBCを見ていきます:/spring-jdbc-jdbctemplate[その他のガイド]。

2 JDBCとGroovyのセットアップ

依存関係の中に _groovy - _ sqlモジュールを含める必要があります。

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy</artifactId>
    <version>2.4.13</version>
</dependency>
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-sql</artifactId>
    <version>2.4.13</version>
</dependency>

groovy-allを使用している場合は、明示的にリストする必要はありません。

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>2.4.13</version>
</dependency>

groovy の最新バージョンを見つけることができます。 groovy-sql および groovy-all Maven上中央です。

3データベースへの接続

データベースを操作するために最初にしなければならないことは、データベースに接続することです。

Groovy SQLモジュールを使用してデータベース上のすべての操作に使用する groovy.sql.Sql クラスを紹介しましょう。

Sql のインスタンスは、操作したいデータベースを表します。

ただし、 Sql のインスタンスは、単一のデータベース接続ではありません** 。

接続については後で説明しますが、今は心配しないでください。すべてが魔法のように機能すると仮定しましょう。

3.1. 接続パラメータの指定

この記事では、HSQLデータベースを使用します。これは主にテストで使用される軽量のリレーショナルDBです。

データベース接続には、URL、ドライバ、およびアクセス認証情報が必要です。

Map dbConnParams =[  url: 'jdbc:hsqldb:mem:testDB',
  user: 'sa',
  password: '',
  driver: 'org.hsqldb.jdbc.JDBCDriver']----

ここでは、__Map__を使用してそれらを指定することにしましたが、それが唯一の可能な選択ではありません。

その後、__Sql__クラスから接続を取得できます。

[source,groovy,gutter:,true]

def sql = Sql.newInstance(dbConnParams)

次のセクションでそれを使用する方法を見ます。

終了したら、常に関連リソースを解放する必要があります。

[source,groovy,gutter:,true]

sql.close()

[[datasource]]

====  **  3.2.  __DataSource__ ** を使用する

データベースに接続するためにデータソースを使用することは、特にアプリケーションサーバー内で実行されているプログラムでは一般的です。

また、接続をプールする場合やJNDIを使用する場合は、データソースが最も自然な選択肢です。

Groovyの__Sql__クラスはデータソースを受け入れます。

[source,groovy,gutter:,true]

def sql = Sql.newInstance(datasource)

[[resource-management]]

====  **  3.3. 自動リソース管理**

__Sql__インスタンスを使い終わったときに__close()__を呼び出すことを忘れないでください。結局のところ、マシンは私たちよりもはるかに良いものを覚えています。

__Sql__を使うと、例外が発生した場合でも、コードをクロージャでラップし、制御が終了すると自動的に__close()__を呼び出すことができます。

[source,groovy,gutter:,true]

Sql.withInstance(dbConnParams) { Sql sql → haveFunWith(sql) }

[[statements]]

===  **  4データベースに対するステートメントの発行**

今、私たちは面白いものに進むことができます。

データベースに対して文を発行する最も単純で特殊化されていない方法は、__execute__メソッドです。

[source,groovy,gutter:,true]

sql.execute "create table PROJECT (id integer not null, name varchar(50), url varchar(100))"

理論的には、DDL/DMLステートメントとクエリの両方に機能します。ただし、上記の単純な形式はクエリ結果を取り戻す方法を提供しません。

後で問い合わせます。

__execute__メソッドには、いくつかのバージョンがオーバーロードされていますが、これ以降のセクションでは、このメソッドと他のメソッドのより高度な使用例について説明します。

[[insert]]

====  **  4.1. データを挿入する**

少量で簡単なシナリオでデータを挿入するには、前述の__execute__メソッドを使用することをお勧めします。

ただし、列を生成した(シーケンスや自動インクリメントなど)ときに生成した値を知りたい場合は、専用のメソッドが存在します。

__execute__については、利用可能な最も単純なメソッドオーバーロードを見ていきます。後のセクションでは、もっと複雑なバリエーションを残します。

したがって、自動インクリメントの主キー(HSQLDBの用語ではアイデンティティー)を持つテーブルがあるとします。

[source,groovy,gutter:,true]

sql.execute "create table PROJECT (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))"

テーブルに行を挿入し、結果を変数に保存しましょう。

[source,groovy,gutter:,true]

def ids = sql.executeInsert """ INSERT INTO PROJECT (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials') """

__executeInsert__は__execute__とまったく同じように動作しますが、何が返されますか?

戻り値は行列であることがわかります。その行は挿入された行(単一のステートメントで複数の行が挿入される可能性があることに注意してください)とその列は生成された値です。

それは複雑に聞こえますが、私たちの場合、これははるかに一般的なもので、単一の行と単一の生成された値があります。

[source,groovy,gutter:,true]

assertEquals(0, ids[0][0])

その後の挿入では、生成された値1が返されます。

[source,groovy,gutter:,true]

ids = sql.executeInsert """ INSERT INTO PROJECT (NAME, URL) VALUES ('REST with Spring', 'github.com/eugenp/REST-With-Spring') """

assertEquals(1, ids[0][0])

[[update]]

====  **  4.2. データの更新と削除**

同様に、データの変更と削除のための専用の方法があります。

__executeUpdate__。

繰り返しになりますが、これは__execute__とは戻り値のみが異なり、最も単純な形式のみを見ていきます。

この場合の戻り値は、影響を受ける行数である整数です。

[source,groovy,gutter:,true]

def count = sql.executeUpdate("UPDATE PROJECT SET URL = 'https://' + URL")

assertEquals(2, count)

[[query]]

===  **  5データベースへの問い合わせ**

データベースに問い合わせると、Groovyが起動します。

JDBCの__ResultSet__クラスを扱うのは、まったく楽しいことではありません。幸いなことに、Groovyはそれをうまく抽象化しています。

[[iterate-query]]

====  **  5.1. クエリ結果の繰り返し**

ループはとても古いスタイルですが...今日は私たちは皆クロージャーに陥っています。

そしてGroovyは私たちの好みに合うようにここにあります:

[source,groovy,gutter:,true]

sql.eachRow("SELECT ** FROM PROJECT") { GroovyResultSet rs → haveFunWith(rs) }

__eachRow__メソッドはデータベースに対してクエリを発行し、各行に対してクロージャを呼び出します。

ご覧のとおり、** 行は__GroovyResultSet__ ** のインスタンスで表されます。これは、いくつかの追加された機能を追加した、普通の古い__ResultSet__の拡張です。それについてもっと知るために読んでください。

[[result-set]]

====  **  5.2. 結果セットへのアクセス**

すべての__ResultSet__メソッドに加えて、__GroovyResultSet__はいくつかの便利なユーティリティを提供します。

主に、列名と一致する名前付きプロパティを公開します。

[source,groovy,gutter:,true]

sql.eachRow("SELECT ** FROM PROJECT") { rs → assertNotNull(rs.name) assertNotNull(rs.URL) }

プロパティ名の大文字と小文字が区別されないことに注意してください。

__GroovyResultSet__は、0から始まるインデックスを使用して列にアクセスすることもできます。

[source,groovy,gutter:,true]

sql.eachRow("SELECT ** FROM PROJECT") { rs → assertNotNull(rs[0]) assertNotNull(rs[1]) assertNotNull(rs[2]) }

====  **  5.3. ページ付け**

結果を簡単にページングできます。つまり、オフセットから最大行数までのサブセットのみをロードできます。これは、たとえばWebアプリケーションでよく見られる問題です。

__eachRow__と関連メソッドは、オフセットと返される最大行数を受け入れるためのオーバーロードを持ちます。

[source,groovy,gutter:,true]

def offset = 1 def maxResults = 1 def rows = sql.rows('SELECT ** FROM PROJECT ORDER BY NAME', offset, maxResults)

assertEquals(1, rows.size()) assertEquals('REST with Spring', rows[0].name)

ここで、__rows__メソッドは__eachRow__のようにそれらを反復するのではなく行のリストを返します。

[[parameters]]

===  **  6. パラメータ化されたクエリと文**

多くの場合、クエリとステートメントはコンパイル時に完全に固定されていません。それらは通常、パラメータの形で静的部分と動的部分を持ちます。

文字列の連結について考えているのなら、ここでやめてSQLインジェクションについて読んでください。

前のセクションで見た方法は、さまざまなシナリオで多くの過負荷があることを先に述べました。

SQLクエリとステートメントのパラメータを扱うオーバーロードを紹介しましょう。

[[string-placeholders]]

====  **  6.1. プレースホルダを持つ文字列**

普通のJDBCに似たスタイルで、位置パラメータを使うことができます。

[source,groovy,gutter:,true]

sql.execute( 'INSERT INTO PROJECT (NAME, URL) VALUES (?, ?)', 'tutorials', 'github.com/eugenp/tutorials')

あるいは、マップで名前付きパラメータを使うことができます。

[source,groovy,gutter:,true]

sql.execute( 'INSERT INTO PROJECT (NAME, URL) VALUES (:name, :url)', [name: 'REST with Spring', url: 'github.com/eugenp/REST-With-Spring'])

これは__execute__、__executeUpdate__、__rows__、および__eachRow__で機能します。

__executeInsert__もパラメータをサポートしていますが、そのシグネチャは少し異なり、よりトリッキーです。

[[gstring]]

====  **  6.2. グルーヴィーな弦**

プレースホルダー付きのGStringsを使用してGroovierスタイルを選択することもできます。

これまで見てきたすべての方法は、GStringsのプレースホルダに代わるものではありません。むしろ、それらはJDBCパラメータとしてそれらを挿入し、SQL構文が正しく維持されていることを保証します。何も引用またはエスケープする必要がなく、したがってインジェクションのリスクもありません。

これは完璧で、安全でGroovyです。

[source,groovy,gutter:,true]

def name = 'REST with Spring' def url = 'github.com/eugenp/REST-With-Spring' sql.execute "INSERT INTO PROJECT (NAME, URL) VALUES (${name}, ${url})"

[[transactions]]

===  **  7. トランザクションと接続

これまでのところ、トランザクションという非常に重要な問題をスキップしました。

実際、Groovyの__Sql__がどのように接続を管理しているのかについても説明していません。

[[short-connections]]

====  **  7.1. 短期間の接続**

これまでに示した例では、** すべてのクエリまたは文は新しい専用の接続を使用してデータベースに送信されました。

もちろん、接続プールを使用している場合は、パフォーマンスへの影響は小さいかもしれません。

それでも、複数のDML文と問合せを単一のアトミック操作として発行する場合は、トランザクションが必要です。

また、トランザクションをそもそも可能にするには、複数のステートメントとクエリにまたがる接続が必要です。

[[transactions-cached]]

====  **  7.2. キャッシュ接続を使用したトランザクション**

Groovy SQLでは、トランザクションを明示的に作成またはアクセスすることはできません。

代わりに、__withTransaction__メソッドをクロージャで使用します。

[source,groovy,gutter:,true]

sql.withTransaction { sql.execute """ INSERT INTO PROJECT (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials') """ sql.execute """ INSERT INTO PROJECT (NAME, URL) VALUES ('REST with Spring', 'github.com/eugenp/REST-With-Spring') """ }

クロージャ内では、単一のデータベース接続がすべてのクエリとステートメントに使用されます。

さらに、トランザクションがクロージャーの終了時に自動的にコミットされます(例外のために早く終了しない限り)。

ただし、__Sql__クラスのメソッドを使用して現在のトランザクションを手動でコミットまたはロールバックすることもできます。

[source,groovy,gutter:,true]

sql.withTransaction { sql.execute """ INSERT INTO PROJECT (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials') """ sql.commit() sql.execute """ INSERT INTO PROJECT (NAME, URL) VALUES ('REST with Spring', 'github.com/eugenp/REST-With-Spring') """ sql.rollback() }

[[cached-connections]]

====  **  7.3. トランザクションなしのキャッシュ接続

最後に、上記のトランザクションセマンティクスなしでデータベース接続を再利用するには、__cacheConnection__を使用します。

[source,groovy,gutter:,true]

sql.cacheConnection { sql.execute """ INSERT INTO PROJECT (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials') """ throw new Exception('This does not roll back') }

[[conclusions]]

===  **  8結論とさらなる読み方**

この記事では、Groovy SQLモジュールと、それがクロージャーとGroovyストリングを使用してJDBCをどのように拡張および簡素化するかについて説明しました。

そして、普通のJDBCはGroovyをふりかけてもう少し現代的に見えると安全に結論づけられます。

Groovy SQLのすべての機能について話したわけではありません。たとえば、/jdbc-batch-processing[バッチ処理]、ストアドプロシージャ、メタデータなどのリンクは省略しました。

詳しくは、http://docs.groovy-lang.org/latest/html/api/groovy/sql/SQL.html[Groovyのドキュメント]を参照してください。

これらすべての例とコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/core-groovy[GitHubプロジェクト]にあります - これはMavenプロジェクトなので、簡単にできます。そのままインポートして実行します。