JPA는 아래와 같은 다양한 쿼리방법을 지원한다.
JPQL은 SQL을 추상화한 객체지향 쿼리이며 문자열인 JPQL을 자바코드로 사용하도록 지원하는 것이 Criteria과 QueryDSL이다.
JPQL은 JPA에서 SQL을 추상화한 쿼리로 SQL문법과 상당히 유사하다. SQL은 테이블을 기준으로 데이터에 접근하지만 JPQL은 객체지향 쿼리여서 엔티티를 기준으로 데이터에 접근한다.
// sql
select * from member;
// jpql
select m from Member m;
select_문 :: =
select_절
from_절
[where_절]
[group by_절]
[having_절]
[order by_절]
update_문 :: = update_절 [where_절]
delete_문 :: = delete_절 [where_절]
TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
Query<Member> query = em.createQuery("select m.username, m.age from Member m");
파라미터 바인딩은 Prepared Statement와 매우 유사하다.
SELECT m FROM Member m where m.username=:username
query.setParameter("username", usernameParam);
SELECT m FROM Member m where m.username=:?1
query.setParameter(1, usernameParam);
추천하는 방식은 이름 기준 바인딩이다. 위치 기준 바인딩은 중간에 파라미터가 추가 된다면 뒤에 번호들도 다 밀어 써야하는 단점이 있다.
프로젝션은 SELECT 절에 조회하르 대상을 지정하는 것으로 엔티티, 임베디드 타입, 스칼라 타입이 있다.
스칼라 타입은 select m.name, m.age from Member m
과 같이 스트링 컬럼, 숫자 컬럼 등 다양한 컬럼을 조회하는 것을 말하는데 이 때 리턴 타입을 특정해서 맞춰주기가 애매하다.
방법으로는 몇 가자가 있는데 개인적으론 DTO를 사용하는 것이 좋아보인다.
JPQL은 페이징을 추상화한 API를 제공하여 DB에 종속적이지 않고 아주 간단하게 페이징 쿼리를 작성할 수 있다.
em.createQuery(SELECT m FROM Member m, Member.class)
.setFristResult(0)
.setMaxResults(10)
JPQL도 조인을 지원하며 SQL과 아주 유사하다.
JPQL도 SQL과 동일하게 조인시 필더링을 위해 ON절을 사용할 수 있다.
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
JPQL은 SQL과 비슷하게 서브쿼리도 지원하지만 하이버네트6 버전 이하는 FROM절에서 서브쿼리는 지원하지 않는다.
때문에 FROM절에 서브쿼리를 사용해야한다면 조인으로 풀어본다, 각각 쿼리를 여러번 날려서 조합한다, 네이티브 SQL을 활용한다 정도가 있다.
나중에 사용한다면 하이버네트6을 사용하도록 해야겠다.
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
select
case t.name
when '팀A' then '인센티브110%'
when '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t
// 엔티티의 m.username이 null이면 '이름 없는 회원'반환
select coalesce(m.username,'이름 없는 회원') from Member m
// 엔티티의 m.username이 관리자면 null반환
select NULLIF(m.username, '관리자') from Member m
JPQL은 기본적인 ANSI함수는 다 제공 된다고 보면 되며, 특정 DB에 종속적이여서 제공되지 않는 함수는 사용자가 추가해서 사용할 수 있다.
// 본인 DB에 맞는 Dialect상속 받아서 구현
public class MyH2Dialect extends H2Dialect {
public MySQLDialect() {
registerFunction("group_concat", new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
}
}
public class CustomFunctionContributor implements FunctionContributor {
@Override
public void contributeFunctions(FunctionContributions functionContributions) {
functionContributions.getFunctionRegistry()
.register("group_concat", new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
}
}