JPQL-프로젝션,조인,조건식
애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요하다.
JPQL은 SQL과 문법이 유사함. 엔티티 객체를 대상으로 쿼리.
JPQL문법
select_문 :: ==
select_절
from_절
[where_절]
[groupby_절]
[having_절]
[orderby_절]
update_문 :: = update_절[where_절]
delete_문 :: = delete_절[where_절]
엔티티와 속성은 대소문자 구분o(Member, age)
JPQL키워드는 대소문자 구분x(select)
엔티티 이름 사용, 테이블 이름이 아님.
별칭은 필수(Member m)
TypeQuery, Query
TypeQuery : 반환 타입이 명확할 때 사용
Query : 반환 타입이 명확하지 않을 때 사용
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class); Query query = em.createQuery("select m.username, m.age from Member m");
결과 조회 API
-query.getResultList() : 결과가 하나 이상일때, 리스트 반환, 결과가 없으면 빈 리스트 반환
-query.getSingleResult() : 결과가 정확히 하나, 객체 반환, 결과가 없어도 에러, 둘 이상이어도 에러,
파라미터 바인딩
Query query = em.createQuery("select m from Member m where m.username=:username");
query.setParameter("username","호호");
검색예시
String jpql = "select m from Member m where m.name like '%hello%'";
List<Member> result = em.createQuery(jpql,Member.class)
.getResultList();
검색예시2
String jpql = "select m from Member m where m.age > 18";
List<Member> result = em.createQuery(jpql,Member.class)
.getResultList();
프로젝션 : select절에 조회할 대상을 지정하는 것
대상 :
- select m from Member m -> 엔티티 프로젝션
- select m.team from Member m -> 엔티티 프로젝션
- select m.address from Member m ->임베디드 타입 프로젝션
DISTINCT로 중복 제거 가능
embedded타입은 from절에 기준 엔티티를 적어줘야함.
List<Address> result = em.createQuery("select o.address from Order o", Address.class)
.getResultList();
프로젝션 - 여러 값 조회
ex) select m.username, m.age from Member m
방법 3가지
- Query타입으로 조회
- Object[]타입으로 조회
- new 명령어로 조회
- 단순 값을 DTO로 바로 조회한다.
- 패키지명을 포함한 전체 클래스명을 입력해야함.
- 순서와 타입이 일치하는 생성자가 필요하다
전체 클래스명 입력하는거 귀찮으면 queryDsl 써야댐. 필드 순서 생성자대로. 틀리면 안됨
List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m",MemberDTO.class)
.getResultList();
toString()만들때 주의) 무한 루프 만들 수 있어서 양방향 컬럼은 지움.
@Entity @Getter @Setter
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
@Override
public String toString() {
return "Member{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
'}';
}
세타조인(=막조인) : 마구잡이 조인
Hibernate 6.1이후부터 from절 subquery도 가능해짐.
조건식 - case
String query =
"select " +
"case when m.age <= 10 then '학생요금' " +
" when m.age >=60 then '경로요금' " +
" else '일반요금' " +
"end " +
"from Member m";
List<String> result = em.createQuery(query, String.class)
.getResultList();
for (String s : result) {
System.out.println("s = " + s);
}
조건식 - nullif() : 관리자 이름 숨길때? 쓸 수 있다(두개 값이 같을 때 null 반환)
String query = "select nullif(m.username, '관리자') as username from Member m";
List<String> result = em.createQuery(query, String.class)
.getResultList();
for (String s : result) {
System.out.println("s = " + s);
}
사용자 정의 함수
1. H2Dialect 상속받은 클래스 생성
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));
}
}
2. persistence.xml에 dialect 경로 수정
<property name="hibernate.dialect" value="dialect.MyH2Dialect"/>
쿼리 이렇게 써도 하이버네이트가 지원해줌.
String query = "select group_concat(m.username) from Member m";
묵시적 조인 쓰지말고 명시적 조인을 써야 쿼리 튜닝하기 좋다.