TIL

객체지향 쿼리 언어 - 중급 문법

경로 표현식

경로 표현식 용어 정리

경로 표현식 특징

“select m.team from Member m” 실행 시 sql에선 조인이 실행됨 (묵시적 내부 조인) 묵시적 내부 조인이 발생하게 짜면 안된다. (운영이 힘들어짐)

“select t.members from Team t” t.members에선 .(점)으로 또 탐색할 수는 없다.

명시직 조인, 묵시적 조인

경로 탐색을 사용한 묵시적 조인 시 주의 사항

실무 조언

JPQL - 페치 조인(fetch join) – 기본

엔티티 페치 조인

페치 조인 사용 코드

String query = "select m from Member m join fetch m.team";

List<Member> result = em.createQuery(query, Member.class)
   .getResultList();

for (Member member : result) {
   System.out.println("userName = " + member.getUserName());
   System.out.println("teamName = " + member.getTeam().getName());
}

컬렉션 페치 조인

select t
from Team t join fetch t.members
where t.name = 팀A'
SELECT T.*, M.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID=M.TEAM_ID
WHERE T.NAME = '팀A'

컬렉션 페치 조인 사용 코드

String query = "select t from Team t join fetch t.members where t.name = '팀A'";

List<Team> teams = em.createQuery(query, Team.class)
   .getResultList();

for(Team team : teams) {
   System.out.println("teamName = " + team.getName() + ", team = " + team);
   for (Member member : team.getMembers()) {
      System.out.println("-> username = " + member.getUserName()+ ", member = " + member);
   }
}
결과
teamname = 팀A, team = Team@0x100 
-> username = 회원1, member = Member@0x200 
-> username = 회원2, member = Member@0x300 
teamname = 팀A, team = Team@0x100 
-> username = 회원1, member = Member@0x200 
-> username = 회원2, member = Member@0x300

이렇게 일대다 관계에서 조인을 날리면 데이터가 뻥튀기 된다. (팀A가 두 번 나옴) 이유는 회원 두 명이 팀A에 매핑된 것을 표현하기 위해 테이블 상에선 위 그림처럼 표현하기 때문이다. 저 테이블에서 팀 컬렉션을 가져오면 팀A는 두 개가 된다. (팀의 회원 수가 엄청 많다면 엄청난 문제..)

페치 조인과 DISTINCT

페치 조인과 일반 조인의 차이

JPQL - 페치 조인(fetch join) – 한계

페치 조인의 특징과 한계

@BatchSize로 N+1 해결하기

String query = "select t from Team t";
List<Team> teams = em.createQuery(query, Team.class)
		.setFirstResult(0)
		.setMaxResults(2)
		.getResultList();
@BatchSize(size = 100)
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();

페치 조인 - 정리

JPQL - 다형성 쿼리

img.png

TYPE

select i from Item i where type(i) IN (Book, Movie)
select i from i where i.DTYPE in (B, M)

TREAT(JPA 2.1)

예) 부모인 Item과 자식 Book이 있다.

select i from Item i where treat(i as Book).auther = kim // 다운 캐스팅과 유사
select i.* from Item i where i.DTYPE = B and i.auther = kim

JPQL - 엔티티 직접 사용

엔티티 직접 사용 - 기본 키 값

엔티티를 파라미터로 전달

String jpql = "**select** m **from** Member m **where** m = :member"; 
List resultList = em.createQuery(jpql)
    .setParameter("member", member)
    .getResultList();

식별자를 직접 전달

String jpql = "**select** m from **Member** m **where** m.id = :memberId"; 
List resultList = em.createQuery(jpql)
    .setParameter("memberId", memberId)
    .getResultList();

엔티티 직접 사용 - 외래 키 값

Team team = em.find(Team.class, 1L);

String qlString = "select m from Member m where m.team = :team";

List resultList = em.createQuery(qlString)
   .setParameter("team", team)
   .getResultList();
String qlString = "select m from Member m where m.team.id = :teamId";
List resultList = em.createQuery(qlString)
   .setParameter("teamId", teamId)
   .getResultList();

JPQL - Named 쿼리 - 정적 쿼리

Named 쿼리 - 어노테이션

@Entity
@NamedQuery(
   name = "Member.findByUserName",
   query = "select m from Member m where m.userName = :username"
)
public class Member {
List<Member> result = em.createNamedQuery("Member.findByUserName", Member.class)
   .setParameter("username", "회원1")
   .getResultList();

JPQL - 벌크 연산

벌크 연산 예제

String qlString = "update Product p " +
   "set p.price = p.price * 1.1 " +
   "where p.stockAmount < :stockAmount";
int resultCount = em.createQuery(qlString)
   .setParameter("stockAmount", 10)
   .executeUpdate();

벌크 연산 주의