"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."
www.inflearn.com/course/ORM-JPA-Basic#
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그
www.inflearn.com
경로 표현식
- . (점) 을 찍어 객체 그래프를 탐색하는 것이다.
예시 :
select m.username -> 상태 필드
from Member m
join m.team t -> 단일 값 연관 필드
join m.orders o -> 컬렉션 값 연관 필드
where t.name = '팀A'
상태 필드로 가냐, 단일 값 연관 필드로 가냐, 컬렉션 값 연관 필드로 가냐에 따라서 내부적으로 동작하는 방식이 달라지기 때문에 결과가 달라지게 된다.
위의 3가지를 반드시 구분해서 이해해야 한다.
- 상태 필드(state field) : 단순히 값을 저장하기 위한 필드
- 연관 필드(association field) : 연관관계를 위한 필드
-> 단일 값 연관 필드 : @ManyToOne, @OneToOne 과 같이 대상이 Entity 인 경우(예 : m.team)
-> 컬렉션 값 연관 필드 : @OneToMany, @ManyToMany 과 같이 대상이 컬렉션 인 경우(m.orders)
* 경로 표현식의 특징
- 상태 필드 : 경로 탐색의 끝으로, 더 이상 탐색을 하지 못한다.
- 단일 값 연관 경로 : 묵시적 내부 조인(inner join) 이 발생하며, 탐색이 가능하다.
아래의 코드를 한 번 살펴보자.
- JpaMain.java
String query = "select m.team From Member m";
// m.team.name 과 같은 탐색이 가능하다.
List<Team> result = em.createQuery(query, Team.class)
.getResultList();
for (Team s : result){
System.out.println("s = " + s);
}
- Hibernate SQL
Hibernate:
/* select
m.team
From
Member m */ select
team1_.id as id1_3_,
team1_.name as name2_3_
from
Member member0_
inner join
Team team1_
on member0_.TEAM_ID=team1_.id
위의 Hibernate SQL 을 잘 보면 from 절에서 member 테이블과 team 테이블을 join 해서 team 데이터를 select 프로젝션에 나열한다.
객체 입장에서는 . 을 찍으면 탐색을 할 수 있지만, DB 입장에서는 join 을 활용해야 한다.
* 이렇게 발생하는 join 을 두고 묵시적 내부 조인이라고 한다.
편해보이지만 join 을 어느 타이밍에 쓰게 될지 명시적으로 보이질 않으므로 유지 보수 및 운영을 하기가 쉽지 않다.
나중에 가서 글을 작성하겠지만 되도록이면 묵시적으로 내부 조인이 발생하게끔 코드를 작성해서는 안된다.
실무에서는 적어도 수백개의 쿼리들이 서로 오고갈 텐데, 조인의 경우는 성능 관리에 있어서 꽤 지대한 영향을 끼치게 된다.
- 컬렉션 값 연관 경로 : 묵시적 내부 조인이 발생하나, 단일 값 연관 경로와는 달리 탐색을 할 수 없다.
컬렉션의 경우 내부에 들어있는 데이터가 1,2 개가 아니기 때문에 그 중에 뭘 선택해서 어떤 필드를 꺼내야 할지 가 난감해진다.
그래서 JPA 에서는 여기에 제약을 걸었다.
컬렉션 값 연관 경로는 묵시적 내부 조인을 발생하나, 더 이상의 탐색은 불가능하다.(중요한 특징)
아래의 코드를 살펴보자.
- JpaMain.java
// 경로 탐색 - 컬렉션 값 연관 경로(탐색 불가능)
String query = "select t.members FROM Team t";
// 여기서 members 는 Team 클래스에서 Member 클래스와 @OneToMany 연관관계로 선언된 컬렉션이다.
Collection result = em.createQuery(query, Collection.class)
.getResultList(); // 컬렉션 반환 값 그 자체를 받아와야 한다.
for (Object o : result) {
System.out.println("o = " + o);
}
- Hibernate SQL
Hibernate:
/* select
t.members
FROM
Team t */ select
members1_.id as id1_0_,
members1_.age as age2_0_,
members1_.TEAM_ID as team_id5_0_,
members1_.type as type3_0_,
members1_.username as username4_0_
from
Team team0_
inner join
Member members1_
on team0_.id=members1_.TEAM_ID
여기서 컬렉션에 값을 넣으면 다음과 같은 결과가 나오는 것을 확인할 수 있다.
- JpaMain.java
Team team = new Team();
team.setName("teamA");
em.persist(team);
Member member = new Member();
member.setUsername("관리자1");
member.setTeam(team);
member.setAge(10);
em.persist(member);
Member member1 = new Member();
member1.setUsername("관리자2");
member1.setAge(20);
member1.setTeam(team);
em.persist(member1);
em.flush();
em.clear();
// 경로 탐색 - 컬렉션 값 연관 경로(탐색 불가능)
String query = "select t.members FROM Team t";
// 여기서 members 는 Team 클래스에서 Member 클래스와 @OneToMany 연관관계로 선언된 컬렉션이다.
Collection result = em.createQuery(query, Collection.class)
.getResultList();
for (Object o : result) {
System.out.println("o = " + o);
}
- Hibernate SQL
Hibernate:
/* select
t.members
FROM
Team t */ select
members1_.id as id1_0_,
members1_.age as age2_0_,
members1_.TEAM_ID as team_id5_0_,
members1_.type as type3_0_,
members1_.username as username4_0_
from
Team team0_
inner join
Member members1_
on team0_.id=members1_.TEAM_ID
o = Member{id=2, username='관리자1', age=10}
o = Member{id=3, username='관리자2', age=20}
그런데 만약 컬렉션 값 연관 경로를 사용하는데 데이터를 탐색하고 싶은 경우, 쿼리를 아래와 같이 FROM 절에서 명시적 내부 조인을 사용하여 별칭을 얻는 방식으로 작성하면, 해당 별칭을 통해 컬렉션 값 연관 경로에서도 탐색이 가능해진다.
String query = "select m.username from Team t join t.members m";
// 위와 같이 명시적 내부 조인으로 m 이라는 별칭을 얻으면 컬렉션 값 연관 경로에서도 필드 탐색이 가능해진다.
* 그런데 강사님이 추천하는 방식은 실무에서는 그냥 묵시적 내부 조인 같은거 쓰지 말고 명시적 내부 조인을 사용하는 것이다.
경로 탐색을 사용한 묵시적 조인 시 주의 사항
- 항상 내부 조인이 수행된다.(외부 조인이 수행되는 것이 아니다.)
- 컬렉션은 경로 탐색의 끝 이므로, 더 탐색을 하고자 한다면 명시적 조인을 통해 별칭을 얻어야 한다.
- 경로 탐색은 주로 SELECT, WHERE 절에서 사용하지만, 묵시적 조인으로 인해 SQL 의 FROM(JOIN) 절에 영향을 준다.
실무 조언?
- 가급적 묵시적 조인 대신 명시적 조인을 사용해라.
- 조인은 SQL 튜닝에 있어 중요한 포인트이다.
- 묵시적 조인은 조인이 일어나는 상황을 한눈에 파악하기 어렵다.
'JPA' 카테고리의 다른 글
JPQL - JPQL 페치 조인(2) (0) | 2020.11.28 |
---|---|
JPQL - JPQL 페치 조인(1) (0) | 2020.11.28 |
JPQL - JPQL 기본(10) (0) | 2020.11.24 |
JPQL - JPQL 기본(9) (0) | 2020.11.24 |
JPQL - JPQL 기본(8) (0) | 2020.11.24 |