環境構築
JPA の基本的な話
マッピングの話
JPQL の話
#クライテリアAPI とは
JPQL をプログラムで動的に生成するための API。
文字列連結で JPQL を生成する場合、構文ミスをコンパイルレベルで検知することができない。
クライテリア API を使えば Java プログラムで JPQL の構築ができるので、 JPQL の構文ミスを回避できる。
#基本
##実装
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
データ
実装
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());
}
}
動作確認
情報: [Kisume{id=1}, Kisume{id=2}]
##説明
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 と同じになる。
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 で追加されたメタモデルの仕組みを利用すれば、そのへんの問題を回避することができるようになる。
##基本
###実装
|-build.gradle
`-src/main/
|-resources/META-INF/
| `-persistence.xml
`-java/sample/jpa/
|-entity/
| `-TestTable.java
`-web/
|-HelloEjb.java
`-HelloServlet.java
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 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>
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 + "]";
}
}
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);
}
}
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();
}
}
データベース
###動作確認
> gradle war
build/libs/jpa-metamodel.war
が出力されるので、 GlassFish にデプロイする。
http://localhost:8080/jpa-metamodel/metamodel
に GET リクエストを送る。
[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]]]
###説明
####メタモデルを使った実装
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_
クラスは以下のようになっている。
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;
}
####コンパイル時にメタモデルを生成させるようにする
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
}
- メタモデルは、注釈処理で自動生成させる。
- 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] が追加されるので、それを選択。
「正規メタモデル(JPA 2.0)」の「ソース・フォルダー」でエンティティを含むソースフォルダを指定する。
これで、エンティティのソースと同じパッケージにメタモデルのソースが出力されるようになる。
#検索条件を構築する
##基本
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
データ
実装
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());
実行結果
情報: [KurodaniYmame{id=2, string=two, number=200}, KurodaniYmame{id=4, string=four, number=400}]
これは、次の JPQL と同じになる。
SELECT k
FROM KurodaniYamame k
WHERE 200 <= k.number
AND k.string LIKE '%o%'
-
CriteriaQuery#where()
で、検索条件を設定する。 - 引数に渡す条件式(
Expression
)は、CriteriaBuilder
に定義されているメソッドを使って作成する。- JPQL に用意されている条件式と対応しているので、メソッド名からだいたい用途を想像できる。 CriteriaBuilder の Javadoc
-
CriteriaBuilder
に定義された条件式を作成するためのメソッドは、基本的に次の引数を受け取る。- 第一引数に比較したい項目。
- 第二引数に比較値。
- 比較したい項目は、
Root#get()
メソッドで取得する。 -
where()
メソッドは条件式を可変長引数で受け取ることができる。- 複数の条件式を受け取った場合、それらは
AND
で結合される。
- 複数の条件式を受け取った場合、それらは
##OR 条件を使用する
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
データ
実装
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());
実行結果
情報: [MizuhashiParsee{id=1, value=hoge}, MizuhashiParsee{id=3, value=piyo}]
JPQL は次と同じになる。
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
から作る。
Root<MizuhashiParsee> root = query.from(MizuhashiParsee.class);
query.select(root)
.where(root.get(MizuhashiParsee_.id).isNotNull())
#パラメータを定義する
##実装
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
データ
実装
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());
実行結果
情報: [HoshigumaYugi{id=2, value=fuga}, HoshigumaYugi{id=3, value=piyo}]
##説明
ParameterExpression<Long> id = builder.parameter(Long.class);
ParameterExpression<String> value = builder.parameter(String.class);
CriteriaBuilder#parameter(Class)
で、 ParameterExpression
のインスタンスを生成する。
query.select(root)
.where(
builder.or(
builder.equal(root.get(HoshigumaYugi_.id), id),
builder.equal(root.get(HoshigumaYugi_.value), value)
)
);
次に、 CriteriaBuilder#equal()
などで条件式を生成するときに、先ほど作成した ParameterExpression
を比較値として設定する。
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
を継承している)。
#エンティティの関連を辿る
##実装
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
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 + '}';
}
}
データ
実装
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());
実行結果
情報: [KomeijiSatori{id=2, komeijiKoishi=KomeijiKoishi{id=2, value=fuga}}]
##説明
上記実装は、下記 JPQL と同じになる。
SELECT k
FROM KomeijiSaori k
WHERE k.komeijiKoishi.value = 'fuga'
query.select(root)
.where(
builder.equal(
root.get(KomeijiSatori_.komeijiKoishi)
.get(KomeijiKoishi_.value),
"fuga"
)
);
-
Root#get()
の戻り値に対して、さらにget()
メソッドを呼び出すことで、エンティティの関連を辿ることができる。
#任意のプロパティを SELECT する
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
データ
##基本
実装
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());
実行結果
情報: [hoge, fuga, piyo]
JPQL に置き換えると以下になる。
SELECT k.string
FROM KaenbyoRin k
-
CriteriaQuery.select()
メソッドの引数に、Root#get()
で取得したプロパティのSelection
を渡すことで、任意のプロパティだけを取得できる。
##Tuple を使って複数のプロパティを取得する
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);
情報: {111, hoge}
情報: {222, fuga}
情報: {333, piyo}
JPQL に置き換えると以下になる。
SELECT k.number
,k.string
FROM KaenbyoRin k
- 検索結果を
Tuple
型で受け取るようにすることで、複数のプロパティを取得できるようになる。-
Tuple
とは、複数の値を組み合わせたものを表している(2つ以上も可)。 -
CriteriaQuery<Tuple>
を取得するための専用のメソッド(createTupleQuery()
)がCriteriaBuilder
に用意されている(createQuery(Tuple.class)
と意味は同じ)。
-
-
CriteriaQuery#select()
メソッドの引数には、CriteriaBuilder#tuple()
で作成したCompoundSelection
インスタンスを渡す。 - 検索結果の
Tuple
から値を取得するときは、Root#get()
メソッドで予め取得しておいたPath
オブジェクトを使うと安全。
#関数の実行結果を SELECT する
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
データ
実装
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());
実行結果
情報: 600
-
CriteriaBuilder
で作成した関数呼び出しの式をCriteriaQuery.select()
メソッドに渡す。
#ORDER BY
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
データ
実装
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);
実行結果
情報: Nazrin{id=2, number=300}
情報: Nazrin{id=1, number=200}
情報: Nazrin{id=3, number=100}
JPQL にすると以下のようになる。
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
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
データ
実装
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);
実行結果
情報: fuga -> 150
情報: hoge -> 80
情報: piyo -> 130
JPQL にすると、以下のようになる。
SELECT t.string
,SUM(t.number)
FROM TataraKogasa t
GROUP BY t.string
-
CriteriaQuery#groupBy(Expression<?>...)
で、集約条件を設定する。 - 検索結果は
Tuple
で受け取るようにすれば、奇麗に値を取得できる。
##HAVING
実装
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);
実行結果
情報: fuga -> 150
情報: piyo -> 130
JPQL にすると以下。
SELECT t.string
,SUM(t.number)
FROM TataraKogasa t
GROUP BY t.string
HAVING 100 < SUM(t.number)
-
CriteriaQuery#having(Expression<Boolean>)
で、HAVING
句を指定できる。
#JOIN
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
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 + '}';
}
}
データ
実装
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);
実行結果
情報: 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 にすると、以下のようになる。
SELECT
DISTINCT ki
FROM KumoiIchirin ki
JOIN ki.unzanList ul
WHERE ul.value LIKE '%e%'
-
Root#join()
メソッドで、 JPQL の JOIN を実行できる。
#FETCH JOIN
実装
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);
実行結果
普通: 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
を使わない場合は以下のようになる。
普通: 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}]}
#サブクエリ
エンティティモデル
実装(エンティティ)
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 + '}';
}
}
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 + '}';
}
}
データ
実装
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);
実行結果
情報: MurasaMinamitsu{id=1}
情報: MurasaMinamitsu{id=3}
JPQL にすると、以下のようになる。
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
エンティティモデル
実装(エンティティ)
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;
}
データ
実装
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();
実行結果
-
UPDATE
文を発行したい場合は、まずCriteriaBuilder#createCriteriaUpdate(Class)
でCriteriaUpdate
のインスタンスを取得する。- 引数の
Class
には、更新対象のエンティティのClass
オブジェクトを渡す。
- 引数の
-
CriteriaUpdate#set()
メソッドで、更新したい項目と、更新後の値を設定する。 -
CriteriaUpdate
の準備が整ったら、EntityManager#createQuery(CriteriaUpdate)
を使ってQuery
のインスタンスを生成する。 - 取得した
Query
のexecuteUpdate()
で更新を実行する。 - 条件の設定は
CriteriaQuery
を使った場合と同様。
##DELETE
エンティティモデル
実装(エンティティ)
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;
}
データ
実装
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();
実行結果
-
DELETE
文を発行する場合は、EntityManager#createQueryDelete(Class)
でCriteriaDelete
インスタンスを取得する。 - あとは、
UPDATE
の場合と同じ。
#参考
- Java Persistence API (JPA) 実践入門 - ひだまりソケットは壊れない
- UserGuide/JPA/Using the Canonical Model Generator (ELUG) - Eclipsepedia
- Java技術最前線 - 「Java SE 6完全攻略」第97回 アノテーションを処理する その4:ITpro
- eclipseでJPAのメタモデルを自動生成する設定 - じゃばらの手記
- JSR-000338 Java Persistence 2.1 Final Release for Evaluation
- hibernate - JPA CriteriaBuilder - How to use "IN" comparison operator - Stack Overflow
- 東方Projectの登場人物 - Wikipedia