JPQL : Java Persistence Query Language
select m from Member as m where m.age > 18
select
COUNT(m),
SUM(m.age),
AVG(m.age),
MAX(m.age),
MIN(m.age)
from Member m
위와 같은 연산이 모두 가능하다.
GROUP BY, HAVING, ORDER BY 또한 가능하다.
TypedQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
TypedQuery<String> query2 = em.createQuery("select m.username from Member m", String.class);
Query query3 = em.createQuery("select m.username, m.age from Member m");
query1에선 member를 조회하니 Member.class로 반환타입이 명확하다.
query2 또한 username을 반환하고, 이 username이 String형이므로 반환타입이 명확하다.
하지만 query3는 String 타입인 username과 int 타입인 age를 반환하므로 반환 타입이 명확하지 않다.
따라서 이렇듯 반환 타입이 명확할 때는 TypeQuery를 사용하고, 반환타입이 명확하지 않을 대는 Query를 사용한다.
TypedQuery<Member> query = em.createQuery("select m from Member m where m.username = :username", Member.class);
query.setParameter("username", "member1");
또는
Member result = em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", "member1")
.getSingleResult();
와 같이 사용한다.
위치 기반의 바인딩도 존재하는데 위치 기반 바인딩은 사용하지 않는 것이 좋다.
Member result = em.createQuery("select m from Member m where m.username = ?1", Member.class)
.setParameter(1, "member1")
.getSingleResult();
SELECT 절에 조회할 대상을 지정하는 것
List<Member> result = em.createQuery("select m from Member m", Member.class)
.getResultList();
위의 코드에서 반환된 result는 영속성 컨텍스트에서 관리가 될까 ? 엔티티 프로젝션을 하면 모두 영속성 컨텍스트에 관리된다.
List<Team> result = em.createQuery("select m.team from Member m", Team.class)
.getResultList();
Team이랑 join하는 쿼리가 나간다.
하지만 JPQL은 최대한 SQL과 비슷하게 적는것이 좋다. 따라서 아래와 같이 하자.
List<Team> result = em.createQuery("select t from Member m join m.team t", Team.class)
위와 같이 하면 join이 예상되기 때문에 join은 명시적으로 하는 것이 좋다.
List<Address> result = em.createQuery("select o.address from Order o", Address.class)
.getResultList();
Address가 소속되어있기 때문에 from Address가 될 수 없는 한계가 있다.
em.createQuery("select m.username, m.age from Member m")
.getResultList();
근데 여기서 타입이 여러개인데 어떻게 조회할까 ?
List resultList = em.createQuery("select m.username, m.age from Member m")
.getResultList();
Object o = resultList.get(0);
Object[] result = (Object[]) o;
System.out.println("username = " + result[0]);
System.out.println("age = " + result[1]);
List<Order[]> resultList = em.createQuery("select m.username, m.age from Member m")
.getResultList();
Order[] result = resultList.get(0);
System.out.println("username = " + result[0]);
System.out.println("age = " + result[1]);
public class MemberDTO {
private String username;
private int age;
public MemberDTO(String username, int age) {
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
.getResultList();
MemberDTO memberDTO = result.get(0);
System.out.println("memberDTO.getUsername() = " + memberDTO.getUsername());
System.out.println("memberDTO.getAge() = " + memberDTO.getAge());
https://inflearn.com/questions/13438
JPA는 페이징을 아래의 두 API로 추상화했다.
List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(1)
.setMaxResults(10)
.getResultList();
JPA는 이렇게 간편하다.
오라클을 사용한 쿼리를 살펴보자.
Hibernate:
/* select
m
from
Member m
order by
m.age desc */ select
*
from
( select
row_.*,
rownum rownum_
from
( select
member0_.id as id1_0_,
member0_.age as age2_0_,
member0_.TEAM_ID as TEAM_ID4_0_,
member0_.username as username3_0_
from
Member member0_
order by
member0_.age desc ) row_ )
where
rownum_ <= ?
and rownum_ > ?
무려 select가 세번이다. 너무 복잡하다.
이렇듯 JPA를 사용하면 정말 간편하게 사용할 수 있다.
SELECT m FROM Member m [INNER] JOIN m.team t
String query = "select m from Member m inner join m.team t";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
String query = "select m from Member m left outer join m.team t";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
select count(m) from Member m, Team t where m.username = t.name
String query = "select m from Member m, Team t where m.username=team.name";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
JPQL:
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
SQL:
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='A'
JPQL:
SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name
SQL:
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name
https://inflearn.com/questions/34345
https://inflearn.com/questions/17488
나이가 평균보다 많은 회원
select m from Member m where m.age > (select avg(m2.age) from Member m2)
메인 쿼리의 Member m 과 별개의 Member m2를 서브쿼리에서 사용
한 건이라도 주문한 고객
select m from Member m where (select count(o) from Order o where m = o.member) > 0
메인 쿼리의 Member m을 서브쿼리에서도 사용
[NOT] EXISTS (subquery)
: 서브쿼리에 결과가 존재하면 참팀A 소속인 회원
select m from Member m
where exists (select t from m.team t where t.name = ‘팀A')
• 전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o
where o.orderAmount > ALL (select p.stockAmount from Product p)
• 어떤 팀이든 팀에 소속된 회원
select m from Member m
where m.team = ANY (select t from Team t)
[NOT] IN (subquery)
: 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참https://inflearn.com/questions/48741
String query = "select m.username, 'HELLO', TRUE from Member m";
List<Object[]> result = em.createQuery(query)
.getResultList();
String query = "select m.username, 'HELLO', TRUE from Member m" +
"where m.type = jpql.MemberType.ADMIN";
List<Object[]> result = em.createQuery(query)
.getResultList();
위와 같이 패키지명을 다 적어줘야하지만, setParmater를 사용하면 패키지명까지 적어주진 않아도 된다.
String query = "select m.username, 'HELLO', TRUE from Member m" +
"where m.type = :userType";
List<Object[]> result = em.createQuery(query)
.setParameter("userType", MemberType.ADMIN)
.getResultList();
em.createQuery("select i from Item i where type(i) == Book ", Item.class);
Book이 Item을 상속받고 있을 때 위와 같이 사용할 수 있다.
SQL과 문법이 같은 식
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
조건과 정확하게 매칭 될 경우이다.
//사용자 이름이 없으면 이름 없는 회원을 반환
String query = "select coalesce(m.username, '이름 없는 회원') from Member m";
//사용자 이름이 '관리자'면 null을 반환하고 나머지는 본인의 이름을 반환
String query = "select nullif(m.username, '관리자') from Member m";
JPQL 함수엔 세가지로 분류된다.
첫번째는 JPQL이 제공하는 표준함수이다. 따라서 DB에 관계없이 사용가능하다.
두번째는 사용자 정의함수이다. DB에 있는 함수를 불러서 사용할 경우
세번째는 DB가 등록해놓은 함수이다. 함수에 따라 다를 수 있기 때문에 함수 종속적이다.
String query = "select concat('a', 'b') from Member m";
String query = "select substring(m.username, 2, 3) from Member m";
String query = "select locate('de','abcdefg') from Member m";
• ABS, SQRT, MOD
• SIZE, INDEX(JPA 용도)
String query = "select size(t.members) from Team t";
size는 컬렉션의 크기를 반환한다.
값타입 컬렉션에서 위치를 알고 싶을 때 @OrderColumn을 사용하는데, 여기의 index도 이때 사용된다.
package dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.StandardBasicTypes;
public class MyH2Dialect extends H2Dialect {
public MyH2Dialect(){
registerFunction("group_concat", new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
}
}
그리고 persistence에 사용하는 db를 H2Dialect가 아닌 MyH2Dialect로 바꾼다.
String query = "select function('group_concat',m.username) from Member m";
위와 같이 사용하면 된다.
group_concate은 데이터를 모두 한줄로 뽑아준다.
하이버네이트를 사용한다면
String query = "select group_concat(m.username) from Member m";
와 같이 사용할 수 있다.