[자바 ORM 표쥰 JPA 프로그래밍] 21일차 - JPQL 활용 (2)

[자바 ORM 표쥰 JPA 프로그래밍] 21일차 - JPQL 활용 (2)

사용된 예시 엔티티

더보기 @Data @Entity public class Team { @Id @Column(name = "TEAM_ID", nullable = false) @GeneratedValue private int id; @Column(name = "TEAM_NAME") private String name; @OneToMany(mappedBy = "team") private List members = new ArrayList(); } @Data @Entity public class Member { @Id @Column(name = "MEMBER_ID", nullable = false) @GeneratedValue private int id; @Column(name = "USER_NAME") private String name; @ManyToOne @JoinColumn(name = "TEAM_ID") private Team team; }

1. 묵시적 조인과 명시적 조인

JPA는 JPQL의 SQL을 최적화시켜 DB에 요청한다.

엔티티 기준으로 JPQL를 작성하게 되는데 SQL과의 차이점을 하나 찾을 수 있다.

// Team 엔티티의 Team 엔티티 값을 조회한다. Query query = em.createQuery("SELECT t FROM Team t"); // 조회된 Team 객체의 값에서 Name 필드만 출력한다. List resultList = query.getResultList(); for (Team team : resultList) { System.out.println("team = " + team.getName()); }

Hibernate: /* SELECT t FROM Team t */ select team0_.TEAM_ID as TEAM_ID1_1_, team0_.TEAM_NAME as TEAM_NAM2_1_ from Team team0_

위의 쿼리는 특별히 문제는 없다. Team 테이블을 전체적으로 조회하고 Team.name의 필드값을 출력하는 간단한 예이다.

// Team 엔티티의 Members 컬렉션 값을 조회한다. Query query = em.createQuery("SELECT t.members FROM Team t");

하지만 이러한 케이스는 JPA에서 Team 엔티티의 members 필드는 컬렉션 값이면서 @OneToMany의 어노테이션을 갖고있다.

결국 Team 엔티티로 부터 members의 엔티티 값을을 호출하기 위해서는 JOIN이 되어 가져올 수 밖에없지만

위의 간단한 JPQL만으로도 자동으로 조인을 해주어 값을 가져오게 된다.

Hibernate: /* SELECT t.members FROM Team t */ select members1_.MEMBER_ID as MEMBER_I1_0_, members1_.USER_NAME as USER_NAM2_0_, members1_.TEAM_ID as TEAM_ID3_0_ from Team team0_ inner join Member members1_ on team0_.TEAM_ID=members1_.TEAM_ID

JPQL에서는 조인을 하진 않았지만 실제 SQL이 생성될때는 JOIN이 되어 호출하게 되는데

이를 묵시적 조인이라고 한다.

// Team 엔티티와 @OneToMany로 조인된 members 컬렉션 값을 명시적 조인하여 SQL를 생성한다. Query query = em.createQuery("SELECT m.name FROM Team t JOIN t.members m WHERE t.name = :teamname"); query.setParameter("teamname", "팀 이름1"); List resultList = query.getResultList(); System.out.println(resultList.get(0));

Hibernate: /* SELECT m.name FROM Team t JOIN t.members m WHERE t.name = :teamname */ select members1_.USER_NAME as col_0_0_ from Team team0_ inner join Member members1_ on team0_.TEAM_ID=members1_.TEAM_ID where team0_.TEAM_NAME=?

그리고 반대로 JPQL에서 JOIN을 명시하여 SQL를 생성하는 것을 보며 명시적 조인이라고 한다.

SQL를 작성해보면 알겠지만 JOIN이 어떻게 작성됐냐에 따라 SQL의 성능이 크게 갈린다.

JPQL은 이를 최소화하여 작성하게된다.

하지만 주의사항이 있는데 JPQL은 묵시적 조인에 대해서는 무조건 INNER JOIN을 사용하기 때문에 주의가 필요하다

+) 추가로

// 아래와 같이 Team 엔티티의 members 컬렉션 값에서 또 접근하여 name 가져오는 것은 불가능하다. Query query = em.createQuery("SELECT t.members.name FROM Team t"); // 이럴때는 JOIN을 명시애햐지만 가져올 수 있다. Query query = em.createQuery("SELECT m.name FROM Team t JOIN t.members m");

위와 같은 케이스를 주의해야하는데 Team 엔티티로부터 members의 컬렉션을 출력하는 SQL 은 간단하게 작성이 가능하지만 또 컬렉션에서 접근하려면 조인을 명시해야한다.

첫번째 쿼리의 경우 JPQL은 이를 감지하지못하고 에러를 출력한다.

from http://tony950620.tistory.com/123 by ccl(A) rewrite - 2021-11-04 05:02:20