わかりやすいJPA(2):DISTINCTオペレータとコンストラクタ式

はじめに

これは、JPAについて解説する連載の2回目の記事です。

さて、SELECT e FROM Employee e で得られる結果は、Employeeオブジェクトのリスト(List)です。また、SELECT e.name FROM Employee e のように基本型(ここではString)の値のリストを返すこともあります。どちらもリストですからプログラムで取り扱いは簡単です。

ただ、SELECT文の実行結果は、利用する前に、何等かの加工をしておくと便利なケースがあります。ここでは、リストに重複した値が含まれる場合に重複を取り除く方法と、エンティティ型ではない値(Object[]型)が返される場合に、それを任意のクラス型のオブジェクトとして受け取る方法について解説します。

解説で出てくるエンティティやクエリの実行については、「連載:わかりやすいJPA」の予備知識 を参照してください。

DISTINCTオペレータ

次のSELECT文は、Employeeエンティティからdepartment(所属部署)を抽出して表示します。つまり、Department型のオブジェクトのListを取得できます。

ただし、何人かは同じ部署に所属しているので、Listの中身は重複しています。次の実行結果を見てください。

(JpqlTestの[SELECT5]のボタンで実行できます)

SELECT e.department FROM Employee e
[ Department [no: 2, name: QA] ]
[ Department [no: 2, name: QA] ]
[ Department [no: 2, name: QA] ]
[ Department [no: 1, name: Engineering] ]
[ Department [no: 1, name: Engineering] ]
[ Department [no: 1, name: Engineering] ]
[ Department [no: 1, name: Engineering] ]
[ Department [no: 1, name: Engineering] ]
[ Department [no: 2, name: QA] ]
[ Department [no: 1, name: Engineering] ]
[ null ]
[ Department [no: 3, name: Accounting] ]
[ Department [no: 3, name: Accounting] ]
※Department(所属)の記載のないEmployeeエンティティでは[null]が表示されます

このような時、重複を除いたListを得たい場合は次のようにSELECT句にDISTINCTオペレーターをつけて実行します。DISTINCTは重複データを除く働きがあります。

(JpqlTestの[SELECT6]のボタンで実行できます)

SELECT DISTINCT e.department FROM Employee e
[ Department [no: 2, name: QA] ]
[ Department [no: 1, name: Engineering] ]
[ null ]
[ Department [no: 3, name: Accounting] ]

コンストラクタ式

前項の例のように、もともとのエンティティの一部のフィールドだけを抽出する操作を、射影(projection)といいます。例えば、次は、Employeeエンティティから、名前(e.name)、給与(e.salary)、所属(e.department.name)の3項目を抽出する射影です。

(JpqlTestの[SELECT8]のボタンで実行できます)

SELECT e.name  e.salary  e.department FROM Employee e
※EmployeeクラスはダウンロードしたJpqlTestプロジェクトにあります。また、ドメインモデルについて解説した「連載:わかりやすいJPA」で必要なツールとドメインモデルについて を参照してください。

残念なことに、このSELECT文で得られるオブジェクトは、もはや Employee型ではありません。要素が3つあるObjectの配列( Object[] )になります。

つまり、1件のデータは {Object , Object, Object} という配列です。このデータを利用するには、Object型の配列を、元の {String, long, Department} という配列に型変換して使わねばなりません。

このような型変換は相応に煩わしいものですから、できるだけ避けたいですね。そこで、いちいち型変換して使うのではなく、name、 salary、 departmentをフィールドに持つ新しいクラスを作成し、そのクラス型のオブジェクトとして射影の結果を受け取る、という方法があります。

それには、結果を受け取るクラス型として、次のようなDetailクラスを作成しておきます。インスタンスを生成できるよう、クラスにはコンストラクタを必ず作成してください。

public class Details {
    private String name;
    private long salary;
    private Department dept;
    
    public Details(String name, long salary, Department dept) {
        this.name = name;
        this.salary = salary;
        this.dept = dept;
    }
    // ゲッター、セッターなど(省略)
}

SELECT文では、このDetailクラスの「コンストラクタ」に結果を受け取り、インスタンスを生成します。これが次に示すコンストラクタ式を使ったSELECT文です。

(このSELECT文は、JpqlTestの[SELECT9]のボタンで実行できます)

SELECT NEW model.Details(e.name  e.salary  e.department) FROM Employee e

これは、SELECT文の抽出結果を、Detailクラスのコンストラクタにセットして、Detail型のオブジェクトとして受け取るという書き方です。

最後に、Detailのようなクラスの作り方と使い方について、次の3点に留意してください。

・使用するクラスは任意に作成してよく、データベースのテーブルは不要
・使用するクラスは、(model.Detailsのように)完全修飾名で指定する
・使用するクラスのコンストラクタは引数並びがSELECT文の指定と一致していること

読者になる

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

%d人のブロガーが「いいね」をつけました。