Querying

★クエリの種類(4種)

動的クエリ 実行時に動的に指定するJPQLクエリ文字列

Query query = em.createQuery("SELECT b FROM book b WHERE b.title = :title");

TypedQuery<Book> query

= em.createQuery("SELECT b FROM book b = ?1", Book.class);

query.setParameter("title", "xxx");

query.setParameter(1, "xxx");

//query.setParameter("date", new java.util.Date(), TemporalType.DATE);

query.setMaxResults(10);

List<Book> books = query.getResultList();

Book book = query.getSingleResult();

Query query = em.createQuery(...).setParameter(...).setMaxResults(10);

名前付きクエリ 静的な名前付きクエリで実行時に変更不可

@Entity

@NamedQuery(name-="book.findAll", query="SELECT b FROM book b")

@NamedQueries({

@NamedQuery(name-="book.findAll", query="SELECT b FROM book b"),

@NamedQuery(name-="book.findByTitle", query="SELECT b FROM book b WHERE b.title = :title")

})

public class Book {

...

}

Query query = em.createNamedQuery("book.findAll");

Query query = em.createNamedQuery("SELECT b FROM book b");

TypedQuery<Book> query = em.createNamedQuery("book.findAll", Book.class);

List<Book> books = query.getResultList();

ネイティブクエリ ネイティブSQL文を実行

Query query = em.createNativeQuery("SELECT * FROM book b", Book.class);

List<Book> books = query.getResultList();

@Entity

@NamedNativeQuery(name-="book.findAll", query="SELECT * FROM book b")

public class Book {

...

}

Query query = em.createNativeQuery("book.findAll");

Criteria API オブジェクト指向のプログラミングによるクエリ

>CriteriaBuilder

createQuery()

createQuery(Class)

createTupleQuery()

createCriteriaDelete(Class) JPA 2.1から

createCriteriaUpdate(Class) JPA 2.1から

>Query clauses

SELECT clause select, distinct, multiselect, array, tuple, construct

FROM clause from, join, fetch

WHERE clause where

GROUP BY / HAVING clauses groupBy, having, count, sum, avg, min, max, ...

ORDER BY clause orderBy, Order, asc, desc

>Query expressions

Literals and Dates literal, nullLiteral, currentDate, ...

Paths, navigation and types get, type

Arithmetic expressions sum, diff, prod, quot, mod, abs, neg, sqrt

String expressions like, length, locate, lower, upper, concat, substring, ...

Collection expressions isEmpty, isNotEmpty, isMember, isNotMember, size

Comparison expressions equal, notEqual, gt, ge, lt, le, between, isNull, ...

Logical expressions and, or, not, isTrue

CriteriaBuilder builder = em.getCriteriaBuilder();

CriteriaQuery<Book> query = builder.createQuery(Book.class);

Root<Book> c = query.from(Book.class);

query.select(c).where(builder.equal(c.get("title"), "xxx"));

query.select(c).where(builder.greaterThan(c.get("id").as(Integer.class), 1001));

サブクエリの例

TypedQuery<Movie> query = entityManager.createQuery(

"SELECT m FROM movie m JOIN FETCH m.product" +

" WHERE m.product.category IN" +

" (SELECT s.xxx FROM sub s WHERE s.yyy >= 300)", Movie.class);

List<Movie> list = query.getResultList();

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

CriteriaQuery<Movie> criteriaQuery =

criteriaBuilder.createQuery(Movie.class);

Root<Movie> from = criteriaQuery.from(Movie.class);

Path<Long> path = from.join("product").get("category");

from.fetch("product");

CriteriaQuery<Movie> select = criteriaQuery.select(from);

Subquery<Long> subquery = criteriaQuery.subquery(Long.class);

Root<Sub> fromSub = subquery.from(Sub.class);

subquery.select(fromSub.get("xxx").as(Long.class));

subquery.where(

criteriaBuilder.ge(fromSub.get("yyy").as(Integer.class), 300));

select.where(criteriaBuilder.in(path).value(subquery));

TypedQuery<Movie> typedQuery = entityManager.createQuery(select);

List<Movie> resultList = typedQuery.getResultList();

assertEquals(list, resultList);

クエリの方法

CriteriaBuilder cb = ...;

// 方法1

CriteriaQuery<CustomerDetails> cq = cb.createQuery(CustomerDetails.class);

Root<Customer> c = cq.from(Customer.class);

cq.where(cb.equal(c.get(Customer_.id), 101));

cq.select(cb.construct(

CustomerDetails.class,

c.get(Customer_.name),

c.get(Customer_.address),

c.get(Customer_.age)));

TypedQuery<CustomerDetails> tq = em.createQuery(cq);

CustomerDetails result = tq.getSingleResult();

// 方法2

CriteriaQuery<Tuple> cq = cb.createTupleQuery();

//CriteriaQuery<Tuple> cq = cb.createQuery(Tuple.class);

Root<Customer> c = cq.from(Customer.class);

cq.where(cb.equal(c.get(Customer_.id), 101));

cq.select(cb.tuple(

c.get(Customer_.name),

c.get(Customer_.address),

c.get(Customer_.age)));

あるいは

cq.multiselect(

c.get(Customer_.name),

c.get(Customer_.address),

c.get(Customer_.age));

TypedQuery<Tuple> tq = em.createQuery(cq);

Tuple result = tq.getSingleResult();

String name = result.get(0, String.class);

Address address = result.get(1, Address.class);

Integer age = result.get(2, Integer.class);

あるいは

cq.multiselect(

c.get(Customer_.name).alias("customerName"),

c.get(Customer_.address).alias("customerAddress"),

c.get(Customer_.age).alias("customerAge"));

TypedQuery<Tuple> tq = em.createQuery(cq);

Tuple result = tq.getSingleResult();

String name = result.get("customerName", String.class);

Address address = result.get("customerAddress", Address.class);

Integer age = result.get("customerAge", Integer.class);

// 方法3

CriteriaBuilder cb = ...;

CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);

Root<Customer> c = cq.from(Customer.class);

cq.where(cb.equal(c.get(Customer_.id), 101));

cq.multiselect(

c.get(Customer_.name),

c.get(Customer_.address),

c.get(Customer_.age));

TypedQuery<Object[]> tq = em.createQuery(cq);

Object[] result = tq.getSingleResult();

★大量データに対する処理

import org.hibernate.*;

import org.hibernate.internal.AbstractQueryImpl;

import javax.persistence.EntityManager;

import javax.persistence.TypedQuery;

@Inject

private EntityManager em;

public <T> void fetchLargeData(

String queryName,

Map<String, Object> parameters,

Class<T> clazz) {

TypedQuery<T> typedQuery = em.createNamedQuery(queryName, clazz);

// HibernateのAPIを使用するためにダウンキャスト

AbstractQueryImpl queryImpl = typedQuery.unwrap(AbstractQueryImpl.class);

String namedSql = queryImpl.getQueryString();

// NOTE: StatelessSessionは関連する永続コンテキストを持たず、一次キャッシュを実装していない。

// (JPA)PersistenceContextにEntityオブジェクトをキャッシュしないDetachedなEntityなので、

// EntityManager#clear()を呼ぶ必要がない。つまり、Entityオブジェクトがメモリに蓄積されない。

Session session = em.unwrap(Session.class);

StatelessSession statelessSession = session.getSessionFactory().openStatelessSession();

// Queryを再生成

SQLQuery sqlQuery = statelessSession.createSQLQuery(namedSql);

for (Map.Entry<String, Object> paremeter : parameters.entrySet()) {

sqlQuery.setParameter(paremeter.getKey(), paremeter.getValue());

}

// メモリ使用量と応答時間を考慮

ScrollableResults results = sqlQuery

.setReadOnly(true)

.setCacheable(false)

.scroll(ScrollMode.FORWARD_ONLY);

while (results.next()) {

T entity = (T) results.get(0);

// NOTE: メモリの解放が不要

}

results.close();

statelessSession.close();

}

★クエリの結果

Query#getSingleResult

Query#getResultList

Query#executeUpdate DELETE and UPDATE

TypedQuery#getSingleResult

TypedQuery#getResultList

TypedQuery<Country> query =

em.createQuery("SELECT c FROM Country c", Country.class);

List<Country> results = query.getResultList();

TypedQuery<Long> query = em.createQuery(

"SELECT COUNT(c) FROM Country c", Long.class);

long countryCount = query.getSingleResult();

int count = em.createQuery("DELETE FROM Country").executeUpdate();

List<Country> results =

query.setFirstResult(pageIndex * pageSize)

.setMaxResults(pageSize)

.getResultList();

★Metamodel API

エンティティクラス

@Entity

public class Order {

@Id

@GeneratedValue

private Integer id;

@ManyToOne

private Customer customer;

@OneToMany

Set<Item> items;

...

}

メタモデルクラス

import javax.persistence.metamodel.SingularAttribute;

import javax.persistence.metamodel.SetAttribute;

import javax.persistence.metamodel.StaticMetamodel;

@StaticMetamodel(Order.class)

public class Order_ {

public static volatile SingularAttribute<Order, Integer> id;

public static volatile SingularAttribute<Order, Customer> customer;

public static volatile SetAttribute<Order, Item> items;

}

補足:Eclipseでメタモデルクラスを自動生成する設定

1.プロジェクトのプロパティを開く

2.Project FacetsでJPAにチェックを付けて、プロジェクトにJPAサポートを追加

3.JPA欄の一番上「Platform」で適切な対象項目を選択

4.JPA欄の一番下「Canonical metamodel(JPA 2.0)」でソースフォルダを設定

※「Discover annotated classes automatically」にチェックすると、エンティティを追加した時に便利

利用例

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery<Order> cq = cb.createQuery(Order.class);

SetJoin<Order, Item> itemNode = cq.from(Order.class).join(Order_.items);

cq.where( cb.equal(itemNode.get(Item_.id), 3 ) ).distinct(true);

Hibernate Metamodel Generator

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-jpamodelgen</artifactId>

<version>...</version>

</dependency>

★Native SQL vs JPQL vs Criteria

全件検索

SELECT b FROM Book b

public List<Book> allEntries() {

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery<Book> cq = cb.createQuery(Book.class);

Root<Book> rootEntry = cq.from(Book.class);

CriteriaQuery<Book> all = cq.select(rootEntry);

TypedQuery<Book> allQuery = em.createQuery(all);

return allQuery.getResultList();

}

public class CompoundElement {

...

public CompoundElement(String name, Double value) {

this.name = name;

this.value = value;

}

//Needed for Criteria

public CompoundElement(String name, Object value) {

this.name = name;

this.value = (Double) value;

}

...

}

Native SQL

SELECT cmf.name, AVG(c.mileage)

FROM Car c

INNER JOIN CarModel cm

ON c.carModel = cm.uuid

INNER JOIN CarManufacturer cmf

ON cm.manufacturer = cmf.uuid

GROUP BY cmf.name

JPQL

public List<CompoundElement> find() {

Query query = em.createQuery(

"SELECT NEW xxx.CompoundElement(cmf.name, AVG(c.mileage)) " +

"FROM Car c " +

"JOIN c.carModel cm " +

"JOIN cm.manufacturer cmf " +

"GROUP BY cmf.name"

);

return (List<CompoundElement>)query.getResultList();

}

Criteria

public List<CompoundElement> find() {

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery<CompoundElement> q =

cb.createQuery(CompoundElement.class);

Root<Car> c = q.from(Car.class);

Join<Car, CarModel> cmJoin = c.join(Car_.carModel);

Join<CarModel, CarManufacturer> cmfJoin =

cmJoin.join(CarModel_.manufacturer);

CompoundSelection<CompoundElement> cSelect =

cb.construct(CompoundElement.class,

cmfJoin.get(CarManufacturer_.name),

cb.avg(c.get(Car_.mileage)));

q.select(cSelect);

q.groupBy(cmfJoin.get(CarManufacturer_.name));

TypedQuery<CompoundElement> query = em.createQuery(q);

return (List<CompoundElement>)query.getResultList();

}

★Initialize lazy relations

Call a method on the mapped relation

Should be avoided because of an additional query

Order order = em.find(Order.class, orderId);

order.getItems().size();

Fetch Join in JPQL

Query q = em.createQuery("SELECT o FROM Order o JOIN FETCH o.items i WHERE o.id = :id");

q.setParameter("id", orderId);

Order order = (Order) q.getSingleResult();

Fetch Join in Criteria API

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery q = cb.createQuery(Order.class);

Root o = q.from(Order.class);

o.fetch("items", JoinType.INNER);

q.select(o);

q.where(cb.equal(o.get("id"), orderId));

Order order = (Order) em.createQuery(q).getSingleResult();

Named Entity Graph (since JPA 2.1)

@Entity

@NamedEntityGraph(name = "graph.Order.items",

attributeNodes = @NamedAttributeNode("items"))

public class Order implements Serializable { ... }

Dynamic Entity Graph (since JPA 2.1)

EntityGraph graph = em.createEntityGraph(Order.class);

Subgraph itemGraph = graph.addSubgraph("items");

Map hints = new HashMap();

hints.put("javax.persistence.loadgraph", graph);

Order order = em.find(Order.class, orderId, hints);

★Criteria Update/Delete (JPA2.1)

public void updateOrder(Double oldAmount, Double newAmount) {

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaUpdate<Order> update = cb.createCriteriaUpdate(Order.class);

Root e = update.from(Order.class);

update.set("amount", newAmount);

update.where(cb.greaterThanOrEqualTo(e.get("amount"), oldAmount));

em.createQuery(update).executeUpdate();

}

public void deleteOrder(Double amount) {

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaDelete<Order> delete = cb.createCriteriaDelete(Order.class);

Root e = delete.from(Order.class);

delete.where(cb.lessThanOrEqualTo(e.get("amount"), amount));

em.createQuery(delete).executeUpdate();

}

★Get JPQL/SQL String From a CriteriaQuery

Hibernate

TypedQuery<Book> query = em.createQuery(...);

query.unwrap(org.hibernate.Query.class).getQueryString() // return SQL string (not JPQL String)

★UML of Criteria API

※他サイトを参照