JPQL을 자바 코드로 작성하도록 도와주는 빌더 클래스 API다. 문자가 아닌 코드로 JPQL을 작성하므로 문법 오류를 컴파일 단계에서 잡을 수 있고 동적 쿼리를 안전하게 생성할 수 있다는 장점이있다.
코드가 복잡하고 장황해서 직관적으로 이해가 힘들다는 단점도 있다.
javax.persistence.criteria 패키지에 있다.
//JPQL : select m from Member m
CriteriaBuilder cb = em.getCriteriaBuilder(); //Criteria 쿼리 빌더
//Criteria 생성, 반환 타입 지정
CriteriaQuery<Member> cq = cb.createQuery(Member.class);
Root<Member> m = cq.from(Member.class); //FROM 절
cq.select(m); // SELECT 절
// 완성된 쿼리를 넣어주면 된다.
TypeQuery<Member> query = em.createQuery(cq);
List<Member> members = query.getResultList();
//JPQL
//select m from Member m
//where m.username='회원1'
//order by m.age desc
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> cq = cb.createQuery(Member.class);
Root<Member> m = cq.from(Member.class); //FROM 절 생성
// 검색 조건 정의
Predicate usernameEqual = cb.equal(m.get("username"), '회원1');
//정렬 조건 정의
javax.persistence.criteria.Order ageDesc = cb.desc(m.get("age"));
//쿼리 생성
cq.select(m)
.where(usernameEqual) //WHERE 절 생성
.orderBy(ageDesc); //ORDER BY 절 생성
List<Member> resultList = em.createQuery(cq).getResultList();
Root<Member> m = cq.from(Member.class);
여기서 m이 쿼리루트//select m from Member m
//where m.age > 10 order by m.age desc
Root<Member> m = cq.from(Member.class);
//타입 정보 필요
Predicate ageGt = cb.greaterThan(m.<Integer>get("age"), 10);
cq.select(m);
cq.where(ageGt);
cq.orderBy(cb.desc(m.get("age")));
public interface CriteriaBuilder {
CriteriaQuery<Object> createQuery(); //조회값 반환 타입 : Object
//조회값 반환 타입 : 엔티티, 임베디드 타입, 기타
<T> CriteriaQuery<T> createQuery(Class<T> resultClass);
CriteriaQuery<Tuple> createTupleQuery(); //조회값 반환 타입: Tuple
}
CirteriaBuilder cb = em.getCriteriaBuilder();
//Member를 반환 타입으로 지정
CriteriaQuery<Member> cq = cb.createQuery(Member.class);
...
//위에서 Member를 타입으로 지정했으므로 지정하지 않아도 Member 타입을 반환
List<Member> resultList = em.createQuery(cq).getResultList();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object> cq = cb.createQuery(); // 조회값 반환 타입 : Object
...
List<Object> resultList = em.createQuery(cq).getResultList();
Criteriabuilder cb = em.getCriteriaBuilder();
//조회값 반환 타입 : Object[]
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
...
List<Object[]> resultList = em.createQuery(cq).getResultList();
CriteriaBuilder cb = em.getCriteriaBuilder();
//조회값 반환 타입 : Tuple
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
...
TypedQuery<Tuple> query = em.createQuery(cq);
//JPQL: select m.username, m.age from Member m
CriteriaBuilder cb = em.getCriteriabuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
//CriteriaQuery<Tuple> cq = cb.createQuery(Tuple.class);
Root<Member> m = cq.from(Member.class);
cq.multiselect(
m.get("username").alias("username"), //튜플에서 사용할 별칭
m.get("age").alias("age")
);
TypedQuery<Tuple> query = em.createQuery(cq);
List<Tuple> resultList = query.getResultList();
for (Tuple tuple : resultList) {
String username = tuple.get("username", String.class);
Integer age = tuple.get("age", Integer.class);
}
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Member> m = cq.from(Member.class);
cq.select(cb.tuple(
m.alias("m"),
m.get("username").alias("username")
));
TypedQuery<Tuple> query = em.createQuery(cq);
List<Tuple> resultList = query.getResultList();
for (Tuple tuple : resultlist) {
Member member = tuple.get("m", Member.class);
String username = tuple.get("username", String.class);
}
cq.select(m) // JPQL : select m
sq.multiselect(m.get("username"), me.get("age"));
CriteriaBuilder cb = em.getCriteriabUILDER();
//JPQL : select m.username, m.age
cq.select(cb.array(m.get("username"), m.get("age")));
select, multiselect다음에 distinct(true)를 사용하면 된다.
JPQL : select distinct m.username, m.age from Member m
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
Root<Member> m = cq.from(Member.class);
cq.multiselect(m.get("username"), m.get("age")).distinct(true);
//cq.select(cb.array(m.get("username"), m.get("age"))).distinct(true);
TypedQuery<Object[]> query = em.createQuery(cq);
List<Object[]> resultList = query.getResultList();
JPQL에서 select new 생성자() 구문을 Criteria에서는 cb.construct(클래스타입, ...)로 사용한다.
//JPQL: select new jpabook.domain.MemberDTO(m.username, m.age)
//from Member m
CriteriaQuery<MemberDTO> cq = cb.createQuery(MemberDTO.class);
Root<Member> m = cq.from(Member.class);
cq.select(cb.construct(MemberDTO.class, m.get("username"), m.get("age")));
TypedQuery<MemberDTO> query = em.createQuery(cq);
List<MemberDTO> resultList = query.getResultList();
//팀 이름별로 나이가 가장 많은 사람과 가장 적은 사람을 구하자
/*
JPQL
select m.team.name, max(m.age), min(m.age)
from Member m
group by m.team.name
*/
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
Root<Member> m = cq.from(Member.class);
Expression maxAge = cb.max(m.<Integer>get("age"));
Expression minAge = cb.min(m.<Integer>get("age"));
cq.multiselect(m.get("team").get("name"), maxAge, minAge);
cq.groupBy(m.get("team").get("name"));
TypedQuery<Object[]> query = em.createQuery(cq);
List<Object[]> resultList = query.getResultList();
cq.multiselect(m.get("team").get("name"), maxAge, minAge)
.groupby(m.get("team").get("name"))
.having(cb.gt(minAge, 10));
cb.desc(...) 또는 cb.asc(...)로 생성할 수 있다.
cq.select(m
.where(ageGt)
.orderBy(cb.desc(m.get("age")));
public enum JoinType {
INNER,
LEFT,
RIGHT
// JPA 구현체나 DB에 따라 지원하지 않을 수 있다.
}
/* JPQL
select m,t from Member m
inner join m.team t
where t.name = '팀A'
*/
Root<Member> m = cq.from(Member.class);
Join<Member, Team> t = m.join("team", JoinType.INNER);
cq.multiselect(m, t)
.where(cb.equal(t.get("name"), "팀A"));
/* JPQL :
select m from Member m
where m.age >=
(select AVG(m2.age) from Member m2)
*/
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> mainQuery = cb.createQuery(Member.class);
//서브쿼리 생성
Subquery<Double> subQuery = mainQuery.subquery(Double.class);
Root<Member> m2 = subQuery.from(Member.class);
subQuery.select(cb.avg(m2.<Integer>get("age")));
Root<Member> m = mainQuery.from(Member.class);
mainQuery.select(m)
.where(cb.ge(m.<Integer>get("age"), subQuery));
/* JPQL
select m from Member m
where exists
(select t from m.team t where t.name='팀A')
*/
Criteriabuilder cb = em.getCriteriabUILDER();
CriteriaQuery<Member> mainQuery = cb.createQuery(Member.class);
// 서브 쿼리에서 사용되는 메인 쿼리의 m
Root<Member> m = mainQuery.from(Member.class);
//서브쿼리의 생성
Subquery<Team> subQuery = mainQuery.subquery(Team.class);
Root<Member> subM = subQuery.correlate(m); //메인 쿼리의 별칭을 가져온다.
Join<Member, Team> t = subM.join("team");
subQuery.select(t)
.where(cb.equal(t.get("name"), "팀A"));
// 메인 쿼리 생성
mainQuery.select(m)
.where(cb.exists(subQuery));
List<Member> resultList = em.createQuery(mainQuery).getResultList();
/* JPQL
select m from Member m
where m.username in ("회원1", "회원2")
*/
CriteriaBuilder cb = em.getCriteriabuilder();
CriteriaQuery<Member> cq = cb.createQuery(Member.class);
Root<Member> m = cq.from(Member.class);
cq.select(m)
.where(cb.in(m.get("username"))
.value("회원1")
.value("회원2"));
selectCase() 메서드와 when(), otherwise() 메서드를 사용한다.
/* JPQL
select m.username,
case when m.age>=60 then 600
when m.age<=15 then 500
else 1000
end
from Member m
*/
Root<Member> m = cq.from(Member.class);
cq.multiselect(
m.get("username"),
cb.selectCase()
.when(cb.ge(m.<Integer>get("age"), 60), 600)
.when(cb.le(m.<Integer>get("age"), 15), 500)
.otherwise(1000)
);
/* JPQL
select m from Member m
where m.username = :usernameParam
*/
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> cq = cb.createQuery(Member.class);
Root<Member> m = cq.from(Member.class);
//정의
cq.select(m)
.where(cb.equal(m.get("username"), cb.parameter(String.class, "usernameParam")));
List<Member> resultList = em.createQuery(cq)
.setParameter("usernameParam", "회원1")
.getResultList();
- Criteria에서는 파라미터를 정의하지 않고 직접 값을 입력해도 실제 SQL에서는 PreparedStatement에 파라미터 바인딩을 사용하고 있다.
//java cq.select(m) .where(cb.equal(m.get("username"), "회원1")); //sql select * from Member m where m.name=?
cb.function(...) 메서드를 사용하면 된다.
Root<Member> m = cq.from(Member.class);
Expression<Long> function = cb.function("SUM", Long.class, m.get("age"));
cq.select(function);