BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース データベースクエリをシンプルにするJinq

データベースクエリをシンプルにするJinq

原文(投稿日:2016/03/21)へのリンク

データベースクエリ用のDSLを提供するライブラリのJinqがJavaとScalaに対応した。.NETのLINQにインスパイアされたこのライブラリは,型安全性のサポートを通じてクエリ記述を容易にすることを目的としたものだ。作者のMing-Yee lu氏の説明によると,Jinqの開発は2006年に開始され,当初はQueryllという名称だった。ただし,強力なソリューションを実装できるようになったのは,ストリームやラムダ式が追加されたJava 8以降である。

Junqが他の既存ライブラリと際立って異なるのは,完全なデータベースアクセス機能を提供していないことだ - Junqはあくまでクエリツールであり,データベースからデータを取得するためだけに使用可能なのだ。挿入や変更,削除といったデータ操作に対しては,開発者が他に何らかのメカニズムを用意しなければならない。このためにJinqでは,JPA互換スキーム(HibernateやEclipseLinkなど)やjOOQなど,一般的なデータベースアクセスライブラリとの連携機能をサポートしている。

既存ライブラリの代替として使用できないことから,Jinqの付加価値を疑問視するユーザも一部にある。その価値がどこにあるのかを確認するため,我々は,さまざまなツールを使用して同じクエリを実行し,比較してみることにした。

世界の国や都市に関するデータベースを想定しよう。そしてその中から,首都に300万人以上の人口を持つ国のリストを取得する必要があるものとしよう。対応するSQLクエリは次のようなものになる。

SELECT country.name
FROM country
JOIN city ON country.capital_id = city.id
WHERE city.population > 3000000

一般的なマッピングと構成を想定すれば,これと同様なクエリは,Hibernateを使用して次のように表すことができる。

List<String> = session.createQuery("SELECT country.name " +
        "FROM country JOIN city " +
        "WHERE city.population > 3000000")
        .list();

ここから分かるように,HQLはクエリの複雑さをいくらか解消してはいるが,文字列で表現されていることに変わりはない。このことは,実行時に始めて発見できるような潜在的エラーの存在する余地があることを示している。次はJOOQを使用して,このクエリを記述する方法を見てみよう。

Result<Record> result = create.select(COUNTRY.NAME)
        .from(COUNTRY)
        .join(CITY).on(COUNTRY.CAPITAL_ID.equal(CITY.ID))
        .where(CITY.POPULATION.gt(3000000))
        .fetch();

ここではプログラマに対して,よりレベルの高い型安全性が提供されている。不正なフィールドやテーブルの参照や操作には,コンパイルエラーが出力される。ただしこのコードでも,まだ流暢であるとは言えない。そこで最後に,Jinqの関数型アプローチを使ってどのように記述されるかを見てみたい。

List<String> = streams.streamAll(em, City.class)
        .where(c -> c.getCountry().getCapital().equals(c)
                && c.getPopulation() > 3000000)
        .select(c -> c.getCountry().getName())
        .toList();

このコードは,開発者がデータストリームを使用して開発する場合の一般的な構造に近く,コード記述時に関数型プログラミングのスタイルを保持することを可能にする。しかしながら,ストリームを扱っているような外見とは違い,内部ではJinqが“シンボリックエグゼキューション(symbolic execution)”と呼ばれるテクニックを駆使して,上記のコードを,データベースが最適化可能な通常のSQLクエリに変換しているのだ,

このコードになると,Jinqはバイトコードレベルでの実行を直接的に行なわず,これらが入力データに与える副作用を計算するという別の方法を選択する - これはシンボリック実行(symbolic execution)と呼ばれる方法だ。副作用の計算が完了したならば,これを等価のSQLクエリに変換する。計算された結果がSQLに変換するには複雑過ぎる場合は,通常のJavaコードとして実行される。これによって結果としては同じになるが,パフォーマンスの低下する可能性がある。

.NETのLINQに相当するものをJavaに求める声は,Java開発コミュニティでは以前からさまざまな部分で存在していた。言語デザインの制約のため,機能をすべて実装することはできなかった – C#などの.NET言語が行なったように,言語の中にLINQを統合するような変更は,Javaのアーキテクトたちがこれまで認めてこなかったものなのだ。この意味で言うならば,Jinqはそれにもっとも近い位置にあるのかも知れない。
 

この記事を評価

関連性
形式

この記事に星をつける

おすすめ度
スタイル

BT