본문 바로가기
학습/DB

경로 표현식

코동이 2021. 10. 5.

* 경로표현식이란?

.(점)을 찍어 객체 그래프를 탐색하는 것

 

상태필드, 단일 값, 컬렉션 값 3가지의 경로 표현식이 있다.

 

select m.username //상태필드
 from Member m
  join m.team t //단일 값 연관필드
  join m.orders o //컬렉션 값 연관필드
where t.name = '팀A';

 

 

  상태필드 연관 엔티티 연관 컬렉션
SELECT 문법 m.username m.team t.members
리턴타입 String Team Collection
추가 경로조회 불가능 가능 불가능
특징 단순한 값 엔티티 1개 엔티티 여러개

 

* 쿼리 비교

 

1. 상태 필드 경로 검색

 

//JPQL
SELECT m.username, m.age FROM Member m

//SQL
SELECT m.username, m.age FROM Member m

 

 

2. 단일 값 연관 엔티티 경로 검색

 

//JPQL
SELECT o.member from Order o

//SQL
SELECT m.* from Order o inner join Mmeber m on o.member_id = m.id

 

 

- 단일 값 연관 필드

@ManyToOne, @OneToOne, 대상이 엔티티(ex: m.team)

 

연관 엔티티 조회는 묵시적 내부조인(inner join)이 발생하며 탐색을 계속 할 수 있다.

연관 엔티티의 필드 조회는 크로스조인(cross join)이 발생한다.

 

...
@Entity
public class Member {
...
@ManyToOne(fetch = FetchType=LAZY)
@JoinColumn(name = "team_id")
private Team team;
...
}

 

 

* 연관 엔티티 조회 

 

String query = "SELECT m.team FROM Member m";
List<Team> result = em.createQuery(query, Team.class)
.getResultList();

for(Team s : result) {
    System.out.println(s);
}

 

...
Hibernate: 
    /* SELECT
        m.team 
    FROM
        Member m */ select
            team1_.id as id1_5_,
            team1_.name as name2_5_ 
        from
            Member member0_ 
        inner join
            Team team1_ 
                on member0_.TEAM_ID=team1_.id
Team{id=1, name='팀A'}

 

Member가 N이므로 연관관계 주인이고 @ManyToOne이므로 연관필드를 조회할 수 있다.

 

 

* 연관 엔티티 필드 조회

 

String query = "SELECT m.team.name FROM Member m";
List<String> result = em.createQuery(query, String.class)
	.getResultList();

for(String s : result) {
    System.out.println(s);
}

 

 

...
Hibernate: 
    /* SELECT
        m.team.name 
    FROM
        Member m */ select
            team1_.name as col_0_0_ 
        from
            Member member0_ cross 
        join
            Team team1_ 
        where
            member0_.TEAM_ID=team1_.id
팀A

 

- 컬렉션 값 연관 필드

@OneToMany, @ManyToMany, 대상이 컬렉션(ex: m.orders)

 

묵시적 내부조인(inner join)이 발생하며 탐색을 계속 할 수 없다.

(FROM 절에서 명시적 조인을 통해 별칭을 얻으면 별칭을 통해 탐색이 가능하다.)

 

* 컬렉션 값 연관 조회

String query3 = "SELECT t.members FROM Team t";
List<Member> result3 = em.createQuery(query3, Member.class)
.getResultList();

for(Member s : result3) {
     System.out.println(s);
}

 

연관 리스트 출력은 Member로 받을 수 없다. 리턴형은 Collection으로 받는다.

 

String query3 = "SELECT t.members FROM Team t";
List<Collection> result3 = em.createQuery(query3, Collection.class)
.getResultList();

for(Object s : result3) {
     System.out.println(s);
}

 

 

...
Hibernate: 
    /* SELECT
        t.members 
    FROM
        Team t */ select
            members1_.id as id1_3_,
            members1_.age as age2_3_,
            members1_.city as city3_3_,
            members1_.street as street4_3_,
            members1_.zipcode as zipcode5_3_,
            members1_.name as name6_3_,
            members1_.TEAM_ID as TEAM_ID9_3_,
            members1_.endDate as endDate7_3_,
            members1_.startDate as startDat8_3_ 
        from
            Team team0_ 
        inner join
            Member members1_ 
                on team0_.id=members1_.TEAM_ID
Member{id=2, name='null', age=0, team=Team{id=1, name='팀A'}}

 

단순한 컬렉션 값 연관 엔티티 조회에서는 필드를 조회할 수 없다. 한계를 극복하기 위해서는 명시적 조인을 사용해야 한다.

 

 

* FROM 절에서 명시적 조인

 

String query4 = "SELECT m FROM Team t JOIN t.members m";
List<Member> result4 = em.createQuery(query4, Member.class)
.getResultList();

for(Member s : result4) {
    System.out.println(s);
}

 

 

.......
Hibernate: 
    /* SELECT
        m 
    FROM
        Team t 
    JOIN
        t.members m */ select
            members1_.id as id1_3_,
            members1_.age as age2_3_,
            members1_.city as city3_3_,
            members1_.street as street4_3_,
            members1_.zipcode as zipcode5_3_,
            members1_.name as name6_3_,
            members1_.TEAM_ID as TEAM_ID9_3_,
            members1_.endDate as endDate7_3_,
            members1_.startDate as startDat8_3_ 
        from
            Team team0_ 
        inner join
            Member members1_ 
                on team0_.id=members1_.TEAM_ID

 

FROM절에서 명시적 조인을 하면, 컬렉션 값 연관 엔티티의 필드를 조회할 수 있다.

 

 

실무에서는 묵시적 조인을 사용하지 않고, 명시적 조인을 사용한다.

 

 

명시적 조인 : join 키워드 직접 사용

 

SELECT t FROM Member m join m.team t

 

 

묵시적 조인 : 경로 표현식에 의해 묵시적으로 SQL 조인 발생(내부 조인만 가능)

 

SELECT m.team FROM Member m

 

 

*예제

 

select o.member.team from Order o

 

성공하지만 엄청난 내부조인이 발생한다.

 

 

select t.members from Team

 

성공하는데, 컬렉션 값이므로 더이상 탐색이 불가능하다.

 

 

select t.members.username from Team t

 

컬렉션 값이므로 t.members에서 더이상 탐색할 수 없다. 탐색하려면 명시적 조인을 사용해야 한다.

 

 

select m.username from Team t join t.members m

 

성공하는데, 컬렉션 값의 필드를 조회하기 위해서 명시적 조인을 사용했다.

 

 

*정리

경로 탐색은 항상 묵시적 조인을 사용하여 내부조인을 발생시킨다. 그래서 JOIN 절에 영향을 준다.

묵시적 조인은 조인이 일어나는 상황을 한눈에 파악하기 어렵다. 묵시적 조인 대신에 명시적 조인을 사용한다.

 

반응형

'학습 > DB' 카테고리의 다른 글

조인  (0) 2021.10.06
프로젝션(엔티티, 임베디드, 스칼라타입)  (0) 2021.10.06
JPA에서 Dirty Check 방지하기  (0) 2021.10.02
영속성 컨텍스트로 발생하는 이슈  (0) 2021.10.01
Converter 사용하기  (0) 2021.09.30