JavaEE使い方メモ（JPA その4 - クライテリアAPI）

[環境構築](http://qiita.com/opengl-8080/items/aeda0d8bad42af3113bd)
[JPA の基本的な話](http://qiita.com/opengl-8080/items/e4840aa3e33b42ae0d6b)
[マッピングの話](http://qiita.com/opengl-8080/items/265f9f66a65e966678cb)
[JPQL の話](http://qiita.com/opengl-8080/items/e074330b5f4862d9995f)

[コード](https://github.com/opengl8080-javaee-samples/jpa)

#クライテリアAPI とは
JPQL をプログラムで動的に生成するための API。

文字列連結で JPQL を生成する場合、構文ミスをコンパイルレベルで検知することができない。
クライテリア API を使えば Java プログラムで JPQL の構築ができるので、 JPQL の構文ミスを回避できる。

#基本
##実装
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/43075d41-d19f-5d1b-0e6d-d464a3c9bec7.jpeg)

**実装（エンティティ）**

```java:Kisume.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Kisume {
    @Id
    private Long id;

    @Override
    public String toString() {
        return "Kisume{" + "id=" + id + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/897acaa7-f99e-a3a8-d04d-72baf726d1e1.jpeg)

**実装**

```java:CriteriaEjb.java
package sample.javaee.jpa.ejb;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import sample.javaee.jpa.entity.criteria.Kisume;

@Stateless
public class CriteriaEjb {

    @PersistenceContext(unitName = "SampleUnit")
    private EntityManager em;
    
    public void hello() {
        CriteriaBuilder builder = this.em.getCriteriaBuilder();
        CriteriaQuery<Kisume> query = builder.createQuery(Kisume.class);
        
        Root<Kisume> root = query.from(Kisume.class);
        query.select(root)
             .where(builder.lessThan(root.get("id"), 3L));
        
        TypedQuery<Kisume> q = this.em.createQuery(query);
        System.out.println(q.getResultList());
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   [Kisume{id=1}, Kisume{id=2}]
```

##説明
```java:CriteriaEjb.java（一部）
CriteriaBuilder builder = this.em.getCriteriaBuilder();
CriteriaQuery<Kisume> query = builder.createQuery(Kisume.class);

Root<Kisume> root = query.from(Kisume.class);
query.select(root)
     .where(builder.lessThan(root.get("id"), 3L));

TypedQuery<Kisume> q = this.em.createQuery(query);
System.out.println(q.getResultList());
```

この実装は、次の JPQL と同じになる。

```sql:
SELECT k
  FROM Kisume k
 WHERE k.id < 3
```

クライテリア API を使うときのおおまかな手順は次のようになる。

- `EntityManager#getCriteriaBuilder()` で `CriteriaBuilder` のインスタンスを取得する。
- `CriteriaBuilder#createQuery()` で `CriteriaQuery` のインスタンスを取得する。
    - このとき、 `createQuery()` の引数には、最終的に取得する検索結果の型（`Class`）を渡す。
- `CriteriaQuery#from()` メソッドで、検索対象のエンティティを指定する。
- `CriteriaQuery#select()` メソッドで、取得する項目（エンティティ）を指定する。
- `CriteriaQuery#where()` メソッドで、細かい検索条件を指定する。
- クエリの構築が完了したら、 `EntityManager#createQuery(CriteriaQuery)` で `Query` のインスタンスを取得する。
- 後は、名前付きクエリを使用したときと同じように `Query#getResultList()` などで結果を取得する。


#メタモデル（Metamodel）
プロパティ名などを文字列で指定していると、エンティティのプロパティ名とかを変更したときに修正漏れが発生するおそれがある。

JPA 2.0 で追加されたメタモデルの仕組みを利用すれば、そのへんの問題を回避することができるようになる。

##基本
###実装
```text:フォルダ構成
|-build.gradle
`-src/main/
   |-resources/META-INF/
   |  `-persistence.xml
   `-java/sample/jpa/
      |-entity/
      |  `-TestTable.java
      `-web/
         |-HelloEjb.java
         `-HelloServlet.java
```

```groovy:build.gradle
apply plugin: 'war'

repositories {
    mavenCentral()
}

dependencies {
    providedCompile 'javax:javaee-api:7.0'
    providedCompile 'org.eclipse.persistence:org.eclipse.persistence.jpa.modelgen.processor:2.5.0'
}

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

compileJava {
    def path = new File(project.projectDir, 'src/main/resources/META-INF/persistence.xml').absolutePath
    options.compilerArgs.addAll '-Aeclipselink.persistencexml=' + path
}

war.baseName = 'jpa-mdetamodel'
```

```xml:persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
  xmlns="http://xmlns.jcp.org/xml/ns/persistence"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
  http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

  <persistence-unit name="SampleUnit">
    <jta-data-source>jdbc/Local_MySQL_test</jta-data-source>
  </persistence-unit>
</persistence>
```

```java:TestTable.java
package sample.jpa.entity;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="test_table")
public class TestTable {
    @Id
    private Long id;
    private String value;
    
    @Override
    public String toString() {
        return "TestTable [id=" + id + ", value=" + value + "]";
    }
}
```

```java:HelloEjb.java
package sample.jpa.web;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import sample.jpa.entity.TestTable;
import sample.jpa.entity.TestTable_;

@Stateless
public class HelloEjb {
    
    @PersistenceContext(unitName="SampleUnit")
    private EntityManager em;
    
    public void metamodel() {
        CriteriaBuilder builder = this.em.getCriteriaBuilder();
        CriteriaQuery<TestTable> query = builder.createQuery(TestTable.class);
        
        Root<TestTable> root = query.from(TestTable.class);
        query.select(root);
        
        Predicate idEqual2 = builder.equal(root.get(TestTable_.id), 2L);
        query.where(idEqual2);
        
        this.em.createQuery(query).getResultList().forEach(System.out::println);
    }
}
```

```java:HelloServlet.java
package sample.jpa.web;

import java.io.IOException;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    @EJB
    private HelloEjb ejb;
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.ejb.hello();
    }
}
```

**データベース**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/880f0ee9-9e77-2c5a-224e-58f0551a842a.jpeg)

###動作確認
```text:ビルド
> gradle war
```

`build/libs/jpa-metamodel.war` が出力されるので、 GlassFish にデプロイする。

`http://localhost:8080/jpa-metamodel/metamodel` に GET リクエストを送る。

```text:GlassFishコンソール出力
[2015-05-26T22:42:42.861+0900] [glassfish 4.1] [INFO] [] [] [tid: _ThreadID=32 _ThreadName=Thread-8] [timeMillis: 1432647762861] [levelValue: 800] [[TestTable [id=2, value=buzz]]]
```

###説明
####メタモデルを使った実装
```java:HelloEjb.java
import sample.jpa.entity.TestTable;
import sample.jpa.entity.TestTable_;

...

    public void metamodel() {
        CriteriaBuilder builder = this.em.getCriteriaBuilder();
        CriteriaQuery<TestTable> query = builder.createQuery(TestTable.class);
        
        Root<TestTable> root = query.from(TestTable.class);
        query.select(root);
        
        Predicate idEqual2 = builder.equal(root.get(TestTable_.id), 2L);
        query.where(idEqual2);
        
        this.em.createQuery(query).getResultList().forEach(System.out::println);
    }
```

- メタモデルの生成を有効にすると、エンティティクラスの名前の末尾にアンダーバー (`_`) が付いたクラスが自動生成される。
    - 上記例の場合、 `TestTable_` クラスが `TestTable` エンティティのメタモデルになる。
- メタモデルにはオリジナルのエンティティが持つものと同じ名前のフィールドが定義されている。
- このフィールドを利用することで、検索条件の構築などを静的に、かつ型安全に実装することができるようになる。

ちなみに、自動生成された `TestTable_` クラスは以下のようになっている。

```java:TestTable_.java
package sample.jpa.entity;

import javax.annotation.Generated;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;

@Generated(value="EclipseLink-2.5.0.v20130507-rNA", date="2015-05-26T22:35:16")
@StaticMetamodel(TestTable.class)
public class TestTable_ { 

    public static volatile SingularAttribute<TestTable, Long> id;
    public static volatile SingularAttribute<TestTable, String> value;

}
```

####コンパイル時にメタモデルを生成させるようにする
```groovy:build.gradle
repositories {
    mavenCentral()
}

dependencies {
    providedCompile 'javax:javaee-api:7.0'
    providedCompile 'org.eclipse.persistence:org.eclipse.persistence.jpa.modelgen.processor:2.5.0'
}

compileJava {
    def path = new File(project.projectDir, 'src/main/resources/META-INF/persistence.xml').absolutePath
    options.compilerArgs.addAll '-Aeclipselink.persistencexml=' + path
}
```

- メタモデルは、[注釈処理](http://qiita.com/opengl-8080/items/beda51fe4f23750c33e9)で自動生成させる。
- GlassFish 4.1 の場合、 JPA の実装は EclipseLink 2.5.0 なので、 EclipseLink が提供しているメタモデル生成用の jar を依存関係に追加する（`org.eclipse.persistence.jpa.modelgen.processor`）。
- EclipseLink でメタモデルの生成を有効にするには、コンパイル時に `-Aeclipselink.persistencexml` オプションに `persistence.xml` へのパスを渡す。

##Eclipse で開発する場合
Eclipse などの IDE で開発する場合、注釈処理がバックグランドで自動で走るようにしておくことで、特に意識することなくメタモデルを利用できるようになる。

「プロジェクト・ファセット」を有効にして、 [JPA] のチェックを入れる。

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/6cca34ca-2b9a-c2f7-e65e-edd10be9a3b0.jpeg)

プロジェクトのプロパティに [JPA] が追加されるので、それを選択。
「正規メタモデル（JPA 2.0）」の「ソース・フォルダー」でエンティティを含むソースフォルダを指定する。

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/68c640d0-dd6b-70fb-c770-eceac441fff8.jpeg)

これで、エンティティのソースと同じパッケージにメタモデルのソースが出力されるようになる。

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/feb5260f-c03f-d26f-1ac6-c33a0a100eb4.jpeg)

#検索条件を構築する
##基本
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/22caf82b-9c2d-c633-c238-80b3ffeb20c8.jpeg)

**実装（エンティティ）**

```java:KurodaniYmame.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="kurodani_yamame")
public class KurodaniYmame {
    @Id
    private Long id;
    private String string;
    private int number;

    @Override
    public String toString() {
        return "KurodaniYmame{" + "id=" + id + ", string=" + string + ", number=" + number + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/2cf96777-6304-4d73-4a7e-6cdf9cd32ac9.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<KurodaniYmame> query = builder.createQuery(KurodaniYmame.class);
    
    Root<KurodaniYmame> root = query.from(KurodaniYmame.class);
    
    query.select(root)
         .where(
             builder.greaterThanOrEqualTo(root.get(KurodaniYmame_.number), 200),
             builder.like(root.get(KurodaniYmame_.string), "%o%")
         );
    
    TypedQuery<KurodaniYmame> q = this.em.createQuery(query);
    System.out.println(q.getResultList());
```

**実行結果**

```text:GlassFishコンソール出力
情報:   [KurodaniYmame{id=2, string=two, number=200}, KurodaniYmame{id=4, string=four, number=400}]
```

これは、次の JPQL と同じになる。

```sql:
SELECT k
  FROM KurodaniYamame k
 WHERE 200 <= k.number
   AND k.string LIKE '%o%'
```

- `CriteriaQuery#where()` で、検索条件を設定する。
- 引数に渡す条件式（`Expression`）は、 `CriteriaBuilder` に定義されているメソッドを使って作成する。
    - JPQL に用意されている条件式と対応しているので、メソッド名からだいたい用途を想像できる。 [CriteriaBuilder の Javadoc][1]
- `CriteriaBuilder` に定義された条件式を作成するためのメソッドは、基本的に次の引数を受け取る。
    - 第一引数に比較したい項目。
    - 第二引数に比較値。
- 比較したい項目は、 `Root#get()` メソッドで取得する。
- `where()` メソッドは条件式を可変長引数で受け取ることができる。
    - 複数の条件式を受け取った場合、それらは `AND` で結合される。

##OR 条件を使用する
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/cf97eab2-1903-ceca-d20c-a65c1864d4b6.jpeg)

**実装（エンティティ）**

```java:MizuhashiParsee.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="mizuhashi_parsee")
public class MizuhashiParsee {
    @Id
    private Long id;
    private String value;

    @Override
    public String toString() {
        return "MizuhashiParsee{" + "id=" + id + ", value=" + value + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a8180275-eaaf-f790-ffd5-ceba64e92191.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<MizuhashiParsee> query = builder.createQuery(MizuhashiParsee.class);
    
    Root<MizuhashiParsee> root = query.from(MizuhashiParsee.class);
    
    query.select(root)
         .where(
             builder.or(
                 builder.equal(root.get(MizuhashiParsee_.id), 1L),
                 builder.equal(root.get(MizuhashiParsee_.id), 3L)
             )
         );
    
    TypedQuery<MizuhashiParsee> q = this.em.createQuery(query);
    System.out.println(q.getResultList());
```

**実行結果**

```text:GlassFishコンソール出力
情報:   [MizuhashiParsee{id=1, value=hoge}, MizuhashiParsee{id=3, value=piyo}]
```

JPQL は次と同じになる。

```sql:
SELECT m
  FROM MizuhashiParsee m
 WHERE m.id = 1
    OR m.id = 3
```

- `CriteriaBuilder#or()` メソッドを使うことで、 `OR` 条件を定義できる。
- 引数は可変長引数になっているので、複数の条件を連結できる。
- `CriteriaBuilder#and()` メソッドもあるので、 `AND` 条件を定義することもできる。

##IN や IS NULL
`IN` や `IS NULL` は `CriteriaBuilder` から作るのではなく、 `Root#get()` で取得した `Path` から作る。

```java:
Root<MizuhashiParsee> root = query.from(MizuhashiParsee.class);

query.select(root)
     .where(root.get(MizuhashiParsee_.id).isNotNull())
```

#パラメータを定義する
##実装
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7aa929fe-a9c7-ee14-930a-8696aa89aae8.jpeg)

**実装（エンティティ）**

```java:HoshigumaYugi.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="hoshiguma_yugi")
public class HoshigumaYugi {
    @Id
    private Long id;
    private String value;

    @Override
    public String toString() {
        return "HoshigumaYugi{" + "id=" + id + ", value=" + value + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/2ebac26f-c66e-90a5-8899-2fc1a06dc6c5.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<HoshigumaYugi> query = builder.createQuery(HoshigumaYugi.class);
    
    Root<HoshigumaYugi> root = query.from(HoshigumaYugi.class);
    
    ParameterExpression<Long> id = builder.parameter(Long.class);
    ParameterExpression<String> value = builder.parameter(String.class);
    
    query.select(root)
         .where(
             builder.or(
                 builder.equal(root.get(HoshigumaYugi_.id), id),
                 builder.equal(root.get(HoshigumaYugi_.value), value)
             )
         );
    
    TypedQuery<HoshigumaYugi> q = this.em.createQuery(query);
    q.setParameter(id, 2L);
    q.setParameter(value, "piyo");
    
    System.out.println(q.getResultList());
```

**実行結果**

```text:GlassFishコンソール出力
情報:   [HoshigumaYugi{id=2, value=fuga}, HoshigumaYugi{id=3, value=piyo}]
```

##説明
```java:パラメータの定義
ParameterExpression<Long> id = builder.parameter(Long.class);
ParameterExpression<String> value = builder.parameter(String.class);
```

`CriteriaBuilder#parameter(Class)` で、 `ParameterExpression` のインスタンスを生成する。

```java:パラメータをクエリに埋め込む
query.select(root)
     .where(
         builder.or(
             builder.equal(root.get(HoshigumaYugi_.id), id),
             builder.equal(root.get(HoshigumaYugi_.value), value)
         )
     );
```

次に、 `CriteriaBuilder#equal()` などで条件式を生成するときに、先ほど作成した `ParameterExpression` を比較値として設定する。

```java:Queryにパラメータの値をセットする
TypedQuery<HoshigumaYugi> q = this.em.createQuery(query);
q.setParameter(id, 2L);
q.setParameter(value, "piyo");
```

最後に、 `Query#<T>setParameter(Parameter<T>, T)` でクエリにパラメータの値をセットする。

第一引数の `Parameter<T>` には、最初に作成した `ParameterExpression<T>` を渡すことができる（`ParameterExpression` は `Parameter` を継承している）。

#エンティティの関連を辿る
##実装
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/9937b6ba-a863-de07-d5b3-2f419f69870b.jpeg)

**実装（エンティティ）**

```java:KomeijiSatori.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="komeiji_satori")
public class KomeijiSatori {
    @Id
    private Long id;
    @JoinColumn(name="komeiji_koishi_id")
    private KomeijiKoishi komeijiKoishi;

    @Override
    public String toString() {
        return "KomeijiSatori{" + "id=" + id + ", komeijiKoishi=" + komeijiKoishi + '}';
    }
}
```

```java:KomeijiKoishi.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="komeiji_koishi")
public class KomeijiKoishi {
    @Id
    private Long id;
    private String value;

    @Override
    public String toString() {
        return "KomeijiKoishi{" + "id=" + id + ", value=" + value + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/83d5fa9f-9c6d-da3b-e06b-7ec7c003f377.jpeg)

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d18fedb4-00e8-ff10-d5de-c8709339d9ec.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<KomeijiSatori> query = builder.createQuery(KomeijiSatori.class);
    
    Root<KomeijiSatori> root = query.from(KomeijiSatori.class);
    
    query.select(root)
         .where(
             builder.equal(
                 root.get(KomeijiSatori_.komeijiKoishi)
                     .get(KomeijiKoishi_.value),
                 "fuga"
             )
         );
    
    TypedQuery<KomeijiSatori> q = this.em.createQuery(query);
    System.out.println(q.getResultList());
```

**実行結果**

```text:GlassFishコンソール出力
情報:   [KomeijiSatori{id=2, komeijiKoishi=KomeijiKoishi{id=2, value=fuga}}]
```

##説明
上記実装は、下記 JPQL と同じになる。

```sql:
SELECT k
  FROM KomeijiSaori k
 WHERE k.komeijiKoishi.value = 'fuga'
```

```java:
query.select(root)
     .where(
         builder.equal(
             root.get(KomeijiSatori_.komeijiKoishi)
                 .get(KomeijiKoishi_.value),
             "fuga"
         )
     );
```

- `Root#get()` の戻り値に対して、さらに `get()` メソッドを呼び出すことで、エンティティの関連を辿ることができる。

#任意のプロパティを SELECT する
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8f2adeb4-6a7f-f3b0-be1a-96a4290e1637.jpeg)

**実装（エンティティ）**

```java:KaenbyoRin.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="kaenbyo_rin")
public class KaenbyoRin {
    @Id
    private Long id;
    private int number;
    private String string;

    @Override
    public String toString() {
        return "KaenbyoRin{" + "id=" + id + ", number=" + number + ", string=" + string + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/60bf2820-21e6-9e66-e205-4ae6d95d57c4.jpeg)


##基本
**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<String> query = builder.createQuery(String.class);
    
    Root<KaenbyoRin> root = query.from(KaenbyoRin.class);
    query.select(root.get(KaenbyoRin_.string));
    
    TypedQuery<String> q = this.em.createQuery(query);
    System.out.println(q.getResultList());
```

**実行結果**

```text:GlassFishコンソール出力
情報:   [hoge, fuga, piyo]
```

JPQL に置き換えると以下になる。

```sql
SELECT k.string
  FROM KaenbyoRin k
```

- `CriteriaQuery.select()` メソッドの引数に、 `Root#get()` で取得したプロパティの `Selection` を渡すことで、任意のプロパティだけを取得できる。

##Tuple を使って複数のプロパティを取得する
```java:CriteriaEjb.java
import javax.persistence.criteria.Path;
import javax.persistence.Tuple;

...

    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<Tuple> query = builder.createTupleQuery();
    
    Root<KaenbyoRin> root = query.from(KaenbyoRin.class);
    Path<Integer> number = root.get(KaenbyoRin_.number)
    Path<String> string = root.get(KaenbyoRin_.string);
    
    query.select(builder.tuple(number, string));
    
    TypedQuery<Tuple> q = this.em.createQuery(query);
    
    q.getResultList()
     .stream()
     .map(tuple -> "{" + tuple.get(number) + ", " + tuple.get(string) + "}")
     .forEach(System.out::println);
```

```text:GlassFishコンソール出力
情報:   {111, hoge}
情報:   {222, fuga}
情報:   {333, piyo}
```

JPQL に置き換えると以下になる。

```sql
SELECT k.number
      ,k.string
  FROM KaenbyoRin k
```


- 検索結果を `Tuple` 型で受け取るようにすることで、複数のプロパティを取得できるようになる。
    - `Tuple` とは、複数の値を組み合わせたものを表している（２つ以上も可）。
    - `CriteriaQuery<Tuple>` を取得するための専用のメソッド（`createTupleQuery()`）が `CriteriaBuilder` に用意されている（`createQuery(Tuple.class)` と意味は同じ）。
- `CriteriaQuery#select()` メソッドの引数には、 `CriteriaBuilder#tuple()` で作成した `CompoundSelection` インスタンスを渡す。
- 検索結果の `Tuple` から値を取得するときは、 `Root#get()` メソッドで予め取得しておいた `Path` オブジェクトを使うと安全。

#関数の実行結果を SELECT する
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7a3b7fb5-422a-ef46-8c56-998bf6abc4f3.jpeg)

**実装（エンティティ）**

```java:ReiujiUtsubo.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="reiuji_utsubo")
public class ReiujiUtsubo {
    @Id
    private Long id;
    private int number;

    @Override
    public String toString() {
        return "ReiujiUtsubo{" + "id=" + id + ", number=" + number + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c3df90cf-757d-f91a-7292-6bb39716f784.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<Integer> query = builder.createQuery(Integer.class);
    
    Root<ReiujiUtsubo> root = query.from(ReiujiUtsubo.class);
    query.select(builder.sum(root.get(ReiujiUtsubo_.number)));
    
    TypedQuery<Integer> q = this.em.createQuery(query);
    System.out.println(q.getSingleResult());
```

**実行結果**

```text:GlassFishコンソール出力
情報:   600
```

- `CriteriaBuilder` で作成した関数呼び出しの式を `CriteriaQuery.select()` メソッドに渡す。

#ORDER BY
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/788877df-e237-6907-4f48-f10689735dc8.jpeg)

**実装（エンティティ）**

```java:Nazrin.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Nazrin {
    @Id
    private Long id;
    private int number;

    @Override
    public String toString() {
        return "Nazrin{" + "id=" + id + ", number=" + number + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/455daeb5-8a4f-658d-a7d9-63bdaaac101b.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<Nazrin> query = builder.createQuery(Nazrin.class);
    
    Root<Nazrin> root = query.from(Nazrin.class);
    query.select(root)
         .orderBy(builder.desc(root.get(Nazrin_.number)));
    
    TypedQuery<Nazrin> q = this.em.createQuery(query);
    q.getResultList().forEach(System.out::println);
```

**実行結果**

```text:GlassFishコンソール出力
情報:   Nazrin{id=2, number=300}
情報:   Nazrin{id=1, number=200}
情報:   Nazrin{id=3, number=100}
```

JPQL にすると以下のようになる。

```sql
  SELECT n
    FROM Nazrin n
ORDER BY n.number DESC
```

- `CriteriaBuilder#asc(Expression)` または `CriteriaBuilder#desc(Expression)` で、 `Order` オブジェクトを生成する。
- 生成した `Order` オブジェクトを、 `CriteriaQuery#orderBy(Order...)` に渡す。

#GROUP BY と HAVING
##GROUP BY
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7b88e9c9-fe8f-26de-5f4a-cef133791974.jpeg)

**実装（エンティティ）**

```java:TataraKogasa.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="tatara_kogasa")
public class TataraKogasa {
    @Id
    private Long id;
    private String string;
    private int number;

    @Override
    public String toString() {
        return "TataraKogasa{" + "id=" + id + ", string=" + string + ", number=" + number + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/99190df8-6c4e-499a-53b1-a62f4c6178f5.jpeg)

**実装**

```java:CriteriaEjb.java
import javax.persistence.criteria.Expression;

...

    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<Tuple> query = builder.createQuery(Tuple.class);
    
    Root<TataraKogasa> root = query.from(TataraKogasa.class);
    Path<String> string = root.get(TataraKogasa_.string);
    Expression<Integer> sum = builder.sum(root.get(TataraKogasa_.number));
    
    query.select(builder.tuple(string, sum))
         .groupBy(string);
    
    TypedQuery<Tuple> q = this.em.createQuery(query);
    
    q.getResultList()
     .stream()
     .map(tuple -> tuple.get(string) + " -> " + tuple.get(sum))
     .forEach(System.out::println);
```

**実行結果**

```text:GlassFishコンソール出力
情報:   fuga -> 150
情報:   hoge -> 80
情報:   piyo -> 130
```

JPQL にすると、以下のようになる。

```sql
  SELECT t.string
        ,SUM(t.number)
    FROM TataraKogasa t
GROUP BY t.string
```

- `CriteriaQuery#groupBy(Expression<?>...)` で、集約条件を設定する。
- 検索結果は `Tuple` で受け取るようにすれば、奇麗に値を取得できる。

##HAVING
**実装**

```diff:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<Tuple> query = builder.createQuery(Tuple.class);
    
    Root<TataraKogasa> root = query.from(TataraKogasa.class);
    Path<String> string = root.get(TataraKogasa_.string);
    Expression<Integer> sum = builder.sum(root.get(TataraKogasa_.number));
    
    query.select(builder.tuple(string, sum))
-        .groupBy(string);
+        .groupBy(string)
+        .having(builder.greaterThan(sum, 100));
    
    TypedQuery<Tuple> q = this.em.createQuery(query);
    
    q.getResultList()
     .stream()
     .map(tuple -> tuple.get(string) + " -> " + tuple.get(sum))
     .forEach(System.out::println);
```

**実行結果**

```text:GlassFishコンソール出力
情報:   fuga -> 150
情報:   piyo -> 130
```

JPQL にすると以下。

```sql:
  SELECT t.string
        ,SUM(t.number)
    FROM TataraKogasa t
GROUP BY t.string
  HAVING 100 < SUM(t.number)
```

- `CriteriaQuery#having(Expression<Boolean>)` で、 `HAVING` 句を指定できる。

#JOIN
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/abd7d390-6b60-061c-0555-b4d944ea2a6c.jpeg)

**実装（エンティティ）**

```java:KumoiIchirin.java
package sample.javaee.jpa.entity.criteria;

import java.util.List;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="kumoi_ichirin")
public class KumoiIchirin {
    @Id
    private Long id;
    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name="ichirin_id")
    private List<KumoiUnzan> unzanList;

    @Override
    public String toString() {
        return "KumoiIchirin{" + "id=" + id + ", unzanList=" + unzanList + '}';
    }
}
```

```java:KumoiUnzan.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="kumoi_unzan")
public class KumoiUnzan {
    @Id
    private Long id;
    private String value;

    @Override
    public String toString() {
        return "KumoiUnzan{" + "id=" + id + ", value=" + value + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/6fe99b7a-dfe7-a54b-08fa-da41fa3e31a1.jpeg)

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/6cda1bb0-6d42-c16d-79d2-65a27beedf2d.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<KumoiIchirin> query = builder.createQuery(KumoiIchirin.class);
    
    Root<KumoiIchirin> root = query.from(KumoiIchirin.class);
    Join<KumoiIchirin, KumoiUnzan> join = root.join(KumoiIchirin_.unzanList);

    query.select(root)
         .distinct(true)
         .where(builder.like(join.get(KumoiUnzan_.value), "%e%"));
    
    TypedQuery<KumoiIchirin> q = this.em.createQuery(query);
    
    q.getResultList()
     .forEach(System.out::println);
```

**実行結果**

```text:GlassFishコンソール出力
情報:   KumoiIchirin{id=1, unzanList=[KumoiUnzan{id=1, value=one}, KumoiUnzan{id=2, value=two}, KumoiUnzan{id=3, value=three}]}
情報:   KumoiIchirin{id=2, unzanList=[KumoiUnzan{id=4, value=four}, KumoiUnzan{id=5, value=five}]}
```

JPQL にすると、以下のようになる。

```sql
  SELECT 
DISTINCT ki
    FROM KumoiIchirin ki
    JOIN ki.unzanList ul
   WHERE ul.value LIKE '%e%'
```

- `Root#join()` メソッドで、 JPQL の JOIN を実行できる。

#FETCH JOIN
**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<KumoiIchirin> query = builder.createQuery(KumoiIchirin.class);
    
    Root<KumoiIchirin> root = query.from(KumoiIchirin.class);
    root.fetch(KumoiIchirin_.unzanList);

    query.select(root)
         .distinct(true);
    
    TypedQuery<KumoiIchirin> q = this.em.createQuery(query);
    
    q.getResultList()
     .forEach(System.out::println);
```

**実行結果**

```text:GlassFishコンソール出力
普通:   SELECT DISTINCT t1.ID, t0.ID, t0.VALUE FROM kumoi_unzan t0, kumoi_ichirin t1 WHERE (t0.ichirin_id = t1.ID)
情報:   KumoiIchirin{id=1, unzanList=[KumoiUnzan{id=1, value=one}, KumoiUnzan{id=2, value=two}, KumoiUnzan{id=3, value=three}]}
情報:   KumoiIchirin{id=2, unzanList=[KumoiUnzan{id=4, value=four}, KumoiUnzan{id=5, value=five}]}
情報:   KumoiIchirin{id=3, unzanList=[KumoiUnzan{id=6, value=six}]}
```

- リスト項目（`unzanList`）の取得が、1 つの SQL で実行されている。
- `Root#fetch()` で、引数で指定したコレクション型のプロパティを `FETCH JOIN` させることができる。

ちなみに、 `FETCH JOIN` を使わない場合は以下のようになる。

```text:GlassFishコンソール出力
普通:   SELECT DISTINCT ID FROM kumoi_ichirin
普通:   SELECT ID, VALUE FROM kumoi_unzan WHERE (ichirin_id = ?)
	bind => [1]
普通:   SELECT ID, VALUE FROM kumoi_unzan WHERE (ichirin_id = ?)
	bind => [2]
普通:   SELECT ID, VALUE FROM kumoi_unzan WHERE (ichirin_id = ?)
	bind => [3]
情報:   KumoiIchirin{id=1, unzanList=[KumoiUnzan{id=1, value=one}, KumoiUnzan{id=2, value=two}, KumoiUnzan{id=3, value=three}]}
情報:   KumoiIchirin{id=2, unzanList=[KumoiUnzan{id=4, value=four}, KumoiUnzan{id=5, value=five}]}
情報:   KumoiIchirin{id=3, unzanList=[KumoiUnzan{id=6, value=six}]}
```

#サブクエリ
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a1aa20e4-93c5-73dc-ba40-77ee4e5518ce.jpeg)

**実装（エンティティ）**

```java:
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="murasa_minamitsu")
public class MurasaMinamitsu {
    @Id
    private Long id;

    @Override
    public String toString() {
        return "MurasaMinamitsu{" + "id=" + id + '}';
    }
}
```

```java:ToramaruShou.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="toramaru_shou")
public class ToramaruShou {
    @Id
    private Long id;

    @Override
    public String toString() {
        return "ToramaruShou{" + "id=" + id + '}';
    }
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/024c04d3-4e67-3752-2bdc-ba2e556ff248.jpeg)

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/fc925f1c-c657-88b5-a9c0-73c6de8c8547.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaQuery<MurasaMinamitsu> query = builder.createQuery(MurasaMinamitsu.class);
    
    Subquery<Long> subquery = query.subquery(Long.class);
    Root<ToramaruShou> toramaru = subquery.from(ToramaruShou.class);
    subquery.select(toramaru.get(ToramaruShou_.id));
    
    Root<MurasaMinamitsu> murasa = query.from(MurasaMinamitsu.class);
    query.select(murasa)
         .where(murasa.get(MurasaMinamitsu_.id).in(subquery));
    
    TypedQuery<MurasaMinamitsu> q = this.em.createQuery(query);
    
    q.getResultList()
     .forEach(System.out::println);
```

**実行結果**

```text:GlassFishコンソール出力
情報:   MurasaMinamitsu{id=1}
情報:   MurasaMinamitsu{id=3}
```

JPQL にすると、以下のようになる。

```sql
SELECT m
  FROM MurasaMinamitsu m
 WHERE m.id IN (
       SELECT t.id
         FROM ToramaruShou t
     )
```

- `CriteriaQuery#subquery(Class)` で、サブクエリを定義するための `Subquery` のインスタンスを取得できる。
    - 引数の `Class` は、最終的にサブクエリが返す値の型を指定する。
- サブクエリの構築は、 `CriteriaQuery` の場合と同じ要領で行う。
- 最後に、でき上がった `Subquery` インスタンスをメインの `CriteriaQuery` の条件式に渡して利用する。

#UPDATE, DELETE
##UPDATE
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8d714ad8-4445-2e30-0440-c2bd7aae25ae.jpeg)

**実装（エンティティ）**

```java:HijiriByakuren.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="hijiri_byakuren")
public class HijiriByakuren {
    @Id
    private Long id;
    private String value;
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/f30a3b3a-ba16-d260-53ab-40c50d3b4f27.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaUpdate<HijiriByakuren> update = builder.createCriteriaUpdate(HijiriByakuren.class);
    
    Root<HijiriByakuren> root = update.from(HijiriByakuren.class);
    
    update.set(root.get(HijiriByakuren_.value), "update!!")
          .where(root.get(HijiriByakuren_.id).in(1, 3));
    
    Query q = this.em.createQuery(update);
    q.executeUpdate();
```

**実行結果**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/cd614045-6a25-22ea-ba3d-19b37682ff41.jpeg)

- `UPDATE ` 文を発行したい場合は、まず `CriteriaBuilder#createCriteriaUpdate(Class)` で `CriteriaUpdate` のインスタンスを取得する。
    - 引数の `Class` には、更新対象のエンティティの `Class` オブジェクトを渡す。
- `CriteriaUpdate#set()` メソッドで、更新したい項目と、更新後の値を設定する。
- `CriteriaUpdate` の準備が整ったら、 `EntityManager#createQuery(CriteriaUpdate)` を使って `Query` のインスタンスを生成する。
- 取得した `Query` の `executeUpdate()` で更新を実行する。
- 条件の設定は `CriteriaQuery` を使った場合と同様。

##DELETE
**エンティティモデル**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/41edbf62-8177-edba-d8e2-70fb404bbc05.jpeg)

**実装（エンティティ）**

```java:HojuNue.java
package sample.javaee.jpa.entity.criteria;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="hojo_nue")
public class HojuNue {
    @Id
    private Long id;
}
```

**データ**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/52dad199-1cb9-1fc5-d982-dc1256e48ba8.jpeg)

**実装**

```java:CriteriaEjb.java
    CriteriaBuilder builder = this.em.getCriteriaBuilder();
    CriteriaDelete<HojuNue> delete = builder.createCriteriaDelete(HojuNue.class);
    
    Root<HojuNue> root = delete.from(HojuNue.class);
    
    delete.where(builder.equal(root.get(HojuNue_.id), 2L));
    
    Query q = this.em.createQuery(delete);
    q.executeUpdate();
```

**実行結果**

![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c8144660-70e8-e7a7-3bdc-bd238b5e0ca6.jpeg)

- `DELETE` 文を発行する場合は、 `EntityManager#createQueryDelete(Class)` で `CriteriaDelete` インスタンスを取得する。
- あとは、 `UPDATE` の場合と同じ。

#参考
- [Java Persistence API (JPA) 実践入門 - ひだまりソケットは壊れない](http://vividcode.hatenablog.com/entry/java/jpa-introduction)
- [UserGuide/JPA/Using the Canonical Model Generator (ELUG) - Eclipsepedia](http://wiki.eclipse.org/UserGuide/JPA/Using_the_Canonical_Model_Generator_%28ELUG%29)
- [Java技術最前線 - 「Java SE 6完全攻略」第97回 アノテーションを処理する その4：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20090129/323775/)
- [eclipseでJPAのメタモデルを自動生成する設定 - じゃばらの手記](http://d.hatena.ne.jp/jabaraster/20110605/1307278919)
- [JSR-000338 Java Persistence 2.1 Final Release for Evaluation](http://download.oracle.com/otndocs/jcp/persistence-2_1-fr-eval-spec/index.html)
- [hibernate - JPA CriteriaBuilder - How to use "IN" comparison operator - Stack Overflow](http://stackoverflow.com/questions/9321916/jpa-criteriabuilder-how-to-use-in-comparison-operator)
- [東方Projectの登場人物 - Wikipedia](http://ja.wikipedia.org/wiki/%E6%9D%B1%E6%96%B9Project%E3%81%AE%E7%99%BB%E5%A0%B4%E4%BA%BA%E7%89%A9)


[1]: http://docs.oracle.com/javaee/7/api/javax/persistence/criteria/CriteriaBuilder.html