Skip to content

Commit 3761eab

Browse files
authored
Merge pull request #68 from 2u6in/main
[6주차/빈] 워크북 제출합니다
2 parents 6db731a + f39287a commit 3761eab

15 files changed

Lines changed: 351 additions & 0 deletions

keyword/chapter06/image.png

115 KB
Loading

keyword/chapter06/keyword.md

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
- JPA란?
2+
3+
# JPA
4+
5+
데이터 베이스의 데이터와 객체를 연결해서 쿼리 작성 대신 객체를 이용해서 데이터를 관리하게 하는 ORM 기술을 사용하는 인터페이스의 모음
6+
7+
→ 인터페이스이므로 Hibernate, EclipseLink 등 구현체를 이용해서 사용
8+
9+
## ORM vs **SQL Mapper(JdbcTemplate, Mybatis)**
10+
11+
- ORM
12+
- db 테이블과 클래스를 매핑 → SQL문을 개발자가 직접 작성하지 않고 코드로 db를 조작
13+
- 장점
14+
- 객체만 매핑하면 쿼리문을 짜주고 jdbc 사용하는 반복적인 일을 대신 해주기 때문에 **생산성이 향상**
15+
- 새로운 칼럼을 추가할 때 필드 하나만 추가하면 됨 → **유지보수 줄어듦**
16+
- SQL Mapper
17+
- 쿼리문을 직접 작성해서 DB를 조작
18+
- 장점
19+
- sql을 마음대로 작성 → 복잡한 쿼리도 자유롭게 작성 가능
20+
- 작성한 대로 sql이 나가기 때문에 어떤 쿼리가 나가는지 명확하고 성능 튜닝이 쉬움
21+
- n+1 문제, 예상하지 못한 쿼리가 생기는 일 없음
22+
- sql 알기만 하면 바로 사용 가능
23+
- 단점
24+
- orm의 장점의 반대가 전부 단점
25+
26+
## JPA 사용 시 주의점
27+
28+
jpa는 **영속성 컨텍스트**를 이용해서 데이터를 관리하기 때문에 성능 최적화
29+
30+
![image.png](image.png)
31+
32+
하지만 영속성 컨텍스트를 잘 이해하지 못하고 사용하면 성능 저하가 발생할 가능성이 높다!
33+
34+
- n+1 문제
35+
- entity를 직접 api 응답으로 반환 시 stackoverflow 발생 가능
36+
- 양방향 연관관계를 가진 엔티티를 json으로 변환 시도한다면 서로 끊임없이 호출하다가 에러남
37+
- 이래서 DTO를 사용
38+
- 즉시로딩 사용의 문제점
39+
- 무의미한 save() 호출
40+
- jpa는 내부에서 Dirty Checking을 통해 변경을 감지하고 트랜잭션이 끝나면 update를 날리므로 데이터 수정 후 save() 호출할 필요가 없음
41+
- N+1 문제란?
42+
43+
# N+1 문제
44+
45+
orm 기술에서 특정 객체를 대상으로 수행한 쿼리가 그 객체의 연관 관계에 있는 n개의 객체들도 조회하게 되어서 n번의 추가적인 쿼리가 발생하게 되는 문제
46+
47+
## 예시
48+
49+
식당과 메뉴 엔티티가 1:N 관계일 때 식당 엔티티가 5개가 있다고 가정
50+
이 때 5개의 식당 정보를 알려고 할 때 코드를 작성하고 실제 실행 되는 쿼리는
51+
52+
```sql
53+
SELECT * FROM restaurant;
54+
55+
SELECT * FROM menu WHERE restaurant_id = 1;
56+
SELECT * FROM menu WHERE restaurant_id = 2;
57+
...
58+
```
59+
60+
이렇게 N번의 쿼리가 더 실행되게 됨
61+
62+
이러면 후에 데이터가 많아졌을 때 장애 요인이 됨
63+
64+
## 원인
65+
66+
식당 엔티티를 조회할 때 테이블을 객체로 맵핑하기 위해 먼저 식당 테이블을 가져오는 쿼리를 날리고 추가로 메뉴 테이블을 조회하는 쿼리를 날려서 식당 테이블을 완성하는 방식으로 작동하기 때문!
67+
68+
## 해결 방법
69+
70+
## **1. outer join fetching**
71+
72+
### 1.1 **fetch join**
73+
74+
```sql
75+
public interface RestaurantRepository extends JpaRepository<Restaurant, Long> {
76+
77+
@Query("select t from restaurant r join fetch r.menu")
78+
List<Team> findAllWithInnerFetchJoin();
79+
80+
@Query("select t from restaurant r left join fetch r.menu")
81+
List<Team> findAllWithOuterFetchJoin();
82+
}
83+
```
84+
85+
→ 모든 식당 데이터를 가져올 때 메뉴도 join해서 가져와서 실제 실행되는 쿼리의 개수가 1
86+
87+
### 1.2 EntityGraph
88+
89+
```sql
90+
public interface RestaurantRepository extends JpaRepository<Restaurant, Long> {
91+
92+
@Query("SELECT r FROM restaurant r")
93+
@EntityGraph(attributePaths = "menu")
94+
List<Restaurant> findAllWithEntityGraph();
95+
}
96+
```
97+
98+
→ 실제로 실행되는 쿼리는 menu를 left join fetch해서 restaurant 엔티티를 가져오는 쿼리기 때문에 쿼리가 1번만 실행
99+
100+
## 2.  **batch fetching**
101+
102+
```sql
103+
@Entity
104+
public class Restaurant{
105+
106+
...
107+
108+
@OneToMany(mappedBy = "restaurant", fetch = FetchType.EAGER)
109+
@BatchSize(size = 5)
110+
private List<Menu> menu = new ArrayList<>();
111+
112+
...
113+
}
114+
115+
```
116+
117+
조회되는 엔티티 위에 @BatchSize를 추가하고 Menu 엔티티가 조회될 때 IN 절을 통해 한번에 조회
118+
119+
→ n번의 조회를 1번으로 줄이는 방식
120+
121+
## 3. **subselect fetching**
122+
123+
```sql
124+
@Entity
125+
public class Restaurant{
126+
127+
...
128+
129+
@OneToMany(mappedBy = "restaurant", fetch = FetchType.EAGER)
130+
@Fetch(value = FetchMode.SUBSELECT)
131+
private List<Menu> menu = new ArrayList<>();
132+
133+
...
134+
}
135+
136+
```
137+
138+
Menu 엔티티가 조회될 때 IN절과 서브 쿼리를 사용하여 한번에 조회
139+
140+
→ n번의 조회를 1번으로 줄이는 방식
141+
142+
**batch & subselect fetching의 공통점**
143+
144+
- LAZY하게 동작하기 때문에 LAZY 로딩에서 선언하기만 하면 상황을 고려할 필요가 없어서 편리
145+
- 그러나 fetch join을 권장
146+
- 편하지만 상황에 따라 쿼리가 달라지는 문제점 존재
147+
- 지연로딩과 즉시로딩의 차이는?
148+
149+
## 지연로딩
150+
151+
연관 관계 지정할 때 `(fetch = FetchType.LAZY)` 을 통해 지연 로딩으로 설정
152+
153+
- 한 엔티티를 조회할 때 해당 엔티티와 연관 관계인 엔티티는 프록시에서 가지고 온다
154+
- 연관된 엔티티는 그 실제 데이터에 접근할 때에야 직접 db에 접근해서 값을 가지고 옴
155+
156+
## 즉시로딩
157+
158+
연관 관계를 지정할 때 `(fetch = FetchType.EAGER)` 를 통해 즉시 로딩으로 설정 가능
159+
160+
- 한 엔티티를 조회할 때 해당 엔티티와 연관관계인 엔티티를 모두 데이터베이스에서함께 불러와서 조회한다
161+
- 연관된 데이터가 항상 필요한 경우 유용하지만, 불필요한 데이터까지 조회할 수 있어 성능 저하의 원인이 될 수 있음
162+
163+
둘 다 n+1 문제가 발생할 수 있으나 지연로딩은 제어 가능하고 즉시 로딩은 제어가 불가능하다 따라서
164+
165+
**실무에서는 기본적으로 지연 로딩을 사용하고 두 가지 엔티티의 정보가 한 번에 필요한 기능에서는 fetch join을 사용해 처리한다**
166+
167+
- JPQL란?
168+
169+
# JPQL
170+
171+
jpa의 일부로 sql처럼 데이터 베이스를 다루는 언어지만 대상이 테이블이 아닌 객체를 대상으로 데이터를 조작하는 언어
172+
173+
## 특징
174+
175+
- sql과 문법이 유사하며 SELECT, INSERT, FROM, WHERE, GROUP BY, HAVING, JOIN을 지원함
176+
- 특정데이터베이스 SQL에 의존하지 않음
177+
- 마지막에는 sql로 변환되어서 작동한다
178+
- 별칭 사용은 필수적
179+
- 테이블 이름이 아닌 엔티티 이름을 사용
180+
- 주의할 점
181+
- 기본 문자열로 작성되어서 문제가 있어도 발견 어려움
182+
183+
## 사용
184+
185+
### **EntityManager 인터페이스**
186+
187+
```java
188+
@PersistenceContext
189+
private EntityManager em; //주입
190+
```
191+
192+
- 모든 결과의 목록을 알고싶을 때 : getResultList()
193+
- 결과가 없으면 빈 리스트 반환
194+
195+
```java
196+
public List<Cafeteria> findAllCafeterias() {
197+
// 1. JPQL 작성 (테이블이 아니라 'Cafeteria' 엔티티 객체를 대상으로 쿼리)
198+
String jpql = "SELECT c FROM Cafeteria c";
199+
200+
// 2. 쿼리 생성 및 실행
201+
return em.createQuery(jpql, Cafeteria.class)
202+
.getResultList(); // 결과가 여러 개(List)일 때 사용
203+
}
204+
```
205+
206+
- 하나의 결과 : getSingleResult()
207+
- 결과가 없거나 2개 이상이면 예외 발생
208+
- 특정 조건으로 검색 (파라미터 바인딩): setParameter()
209+
210+
```java
211+
public Cafeteria findCafeteriaByName(String searchName) {
212+
String jpql = "SELECT c FROM Cafeteria c WHERE c.name = **:name**"; // :name 이 파라미터 자리
213+
214+
return em.createQuery(jpql, Cafeteria.class)
215+
**.setParameter("name", searchName)** // :name 자리에 searchName 변수 값을 바인딩
216+
.getSingleResult(); // 결과가 정확히 1개일 때 사용
217+
}
218+
```
219+
220+
- 하나의 쿼리를 사용하여 다수의 데이터를 변경 (벌크 연산): executeUpdate()
221+
222+
```java
223+
public int updateAllMealsPrice(int increaseAmount) {
224+
String jpql = "UPDATE Meal m SET m.price = m.price + :amount";
225+
226+
return em.createQuery(jpql)
227+
.setParameter("amount", increaseAmount)
228+
.executeUpdate(); // 영향을 받은(수정/삭제된) 데이터의 개수를 반환합니다.
229+
}
230+
```
231+
232+
233+
- Fetch Join란?
234+
235+
# Fetch Join
236+
237+
JPA에서 엔티티를 조회할 때 연관된 엔티티를 처음부터 함께 로딩하는 방식
238+
239+
```sql
240+
public interface RestaurantRepository extends JpaRepository<Restaurant, Long> {
241+
242+
@Query("select t from restaurant r **join fetch** r.menu")
243+
List<Team> findAllWithInnerFetchJoin();
244+
245+
@Query("select t from restaurant r left **join fetch** r.menu")
246+
List<Team> findAllWithOuterFetchJoin();
247+
}
248+
```
249+
250+
- 특징
251+
- 위에 나왔던 n+1 문제를 해결하기 위해 사용
252+
- sql의 조인의 종류가 아니라 jpql에서 성능 최적화를 위해 제공하는 기능
253+
- 다음과 같이 동작한다.
254+
- JPQL
255+
- `SELECT m FROM Member m JOIN FETCH m.team`
256+
- SQL
257+
- `SELECT m.*, t.* FROM Member m INNER JOIN Team t ON m.team_id=t.id`
258+
- 일반 join과 fetch join의 차이
259+
- 일반 Join
260+
- Fetch Join과 달리 연관 Entity에 Join을 걸어도 실제 쿼리에서 SELECT 하는 Entity는오직 JPQL에서 조회하는 주체가 되는 Entity만 조회하여 영속화
261+
- 조회의 주체가 되는 Entity만 SELECT 해서 영속화하기 때문에 데이터는 필요하지 않지만 연관 Entity가 검색조건에는 필요한 경우에 주로 사용
262+
- fetch join
263+
- 조회의 주체가 되는 Entity 이외에 Fetch Join이 걸린 연관 Entity도 함께 SELECT 하여 모두 영속화
264+
- Fetch Join은 연관 엔티티를 한 번의 JOIN 쿼리로 함께 조회해 영속성 컨텍스트에 미리 로딩하므로, 지연로딩이어도 추가 쿼리가 발생하지 않아 N+1 문제가 발생하지 않음
265+
- @EntityGraph란?
266+
267+
# EntityGraph
268+
269+
엔티티를 조회할 때 연관된 엔티티를 어떤 방식으로 가져올지 정의하는 방법
270+
271+
## 사용 이유
272+
273+
- N+1 문제 해결
274+
- JPQL 대체
275+
- 복잡한 jpql 사용하지 않고 연관 데이터 가져올 수 있음
276+
- 선택적 로딩
277+
- 필요에 따라 연관된 특정 데이터만 가져올 수 있어서 성능 최적화 가능
278+
279+
## 사용 예시
280+
281+
```java
282+
//공통 메서드 오버라이드
283+
@Override
284+
@EntityGraph(attributePaths = {"team"})
285+
List<Member> findAll();
286+
287+
//JPQL + 엔티티 그래프
288+
@EntityGraph(attributePaths = {"team"})
289+
@Query("select m from Member m")
290+
List<Member> findMemberEntityGraph();
291+
292+
//메서드 이름으로 쿼리
293+
@EntityGraph(attributePaths = {"team"})
294+
List<Member> findByUsername(String username)
295+
```
296+
297+
- 이렇게 하면 JPQL 없이 페치 조인을 사용할 수 있다
298+
- left outer join을 사용해서 fetch join을 하는 방식
299+
- commit과 flush 차이점은?
300+
301+
## 공통점
302+
303+
- 영속성 컨텍스트에서의 변경 사항을 데이터베이스와 동기화하는 과정에 관여
304+
305+
## 차이점
306+
307+
### flush
308+
309+
- 영속성 컨텍스트의 변경사항을 **즉시 데이터베이스에 반영**
310+
- 1차 캐시나 영속성 컨텍스트 안에 있는 엔티티들은 그대로 유지
311+
- 비우는 역할은 clear()
312+
- 해당 트랜잭션을 바로 커밋하지 않음
313+
- ROLLBACK을 사용해서 데이터베이스를 반영 전으로 되돌리기 가능
314+
- 자동 실행 시점
315+
- 트랜잭션 커밋 시 (commit 내부 동작)
316+
- JPQL 쿼리 실행 시
317+
- 사용 목적
318+
- 변경 사항을 즉시 데이터베이스에 반영해야 하지만 트랜잭션은 유지해야 할 때 사용
319+
- JPQL 실행 전에 변경 사항을 강제 반영해야 할 때 사용
320+
321+
### commit
322+
323+
- 현재 트랜잭션을 완료하고 모든 변경 사항을 확정
324+
- 내부적으로 **flush 실행 후 바로 트랜잭션을 커밋**하는 과정
325+
- 이후 rollback(트랜잭션 취소할 때 사용) 불가
326+
- 다른 트랜잭션에서도 변경 사항을 볼 수 있게함
327+
- flush를 통해 DB에 쿼리가 전송되어 데이터가 조작되었더라도, commit이 발생하기 전까지는 다른 DB 커넥션이나 다른 트랜잭션에게는 그 변경된 데이터가 보이지 않음

mission/chapter06/home (2).png

53.5 KB
Loading

mission/chapter06/home (3).png

53.7 KB
Loading

mission/chapter06/home.png

66.3 KB
Loading

mission/chapter06/mission.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# PR review
2+
![pr1.png](pr1.png)
3+
![pr2.png](pr2.png)
4+
5+
# 리뷰 작성 api
6+
7+
![review.png](review.png)
8+
![review (2).png](review%20%282%29.png)
9+
10+
# 마이페이지 api
11+
12+
![mypage.png](mypage.png)
13+
![mypage (2).png](mypage%20%282%29.png)
14+
15+
# 미션 리스트 api
16+
![missionlist.png](missionlist.png)
17+
![missionlist (2).png](missionlist%20%282%29.png)
18+
![missionlist (3).png](missionlist%20%283%29.png)
19+
20+
# 홈화면 api
21+
22+
![home.png](home.png)
23+
![home (2).png](home%20%282%29.png)
24+
![home (3).png](home%20%283%29.png)
61.1 KB
Loading
64.2 KB
Loading

mission/chapter06/missionlist.png

66.7 KB
Loading

mission/chapter06/mypage (2).png

61.6 KB
Loading

0 commit comments

Comments
 (0)