Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 백준
- 형상관리
- S3
- aop
- multimodule testcontainers
- springsecurity
- kotest testcontainers
- spring aop
- spring DI
- RefreshToken
- 소수찾기 java
- 멀티모듈 테스트컨테이너
- netty
- interface
- ObjectOptimisticLockingFailureException
- java
- 알고리즘
- ObjectOptimisticLockingFailureException 처리
- TestContainers
- DI
- jpa
- 낙관적 락 롤백
- @transactional
- AccessToken
- Spring Cloud Gateway
- Invalid property 'principal.username' of bean class
- 우아한 테크러닝
- redissonlock aop
- 낙관적 락 재시도
- OptimisticLock
Archives
- Today
- Total
조급하면 모래성이 될뿐
N + 1 직접 만났다 본문
Entity 관계
- 1개의 스케줄에는 예매된 N개의 예매 좌석 정보가 존재한다.
- ex) A영화관 B영화의 C 상영관의 13:00에 예매된 좌석은 0, 1, 2이다.
- 예매 좌석 정보와 좌석 정보(seat)와 1:N 관계를 맺고 있다.
- ReservedSeat(1)에서 Seat(N) 정보를 가져온다.
의도한 것
- 스케줄 id로 검색해서 예약 좌석 정보를 가져온다 (ReservedSeat)
- 좌석정보를 내려주기 위해 ReservedSeat에 포함된 Seat객체를 dto객체로 convert 한다.
AS-IS
- ReservedSeatService.java
public List<ResponseFindSeat> findByScheduleId(Long scheduleId) {
return reservedSeatRepository.searchByIdStartsWith(makeFindByScheduleParam(scheduleId)).stream()
.map((reservedSeat -> seatConverter.convertFromSeatToResponseFindSeat(reservedSeat.getSeat())))
.collect(toList());
}
- ReservedSeatRepository
- 그냥 named 쿼리 수행했다.
public interface ReservedSeatRepository extends JpaRepository<ReservedSeat, String> {
List<ReservedSeat> searchByIdStartsWith(String scheduleId);
}
TO-BE
- fetch join으로 한 번에 seat도 함께 가져오도록 변경했다.
public interface ReservedSeatRepository extends JpaRepository<ReservedSeat, String> {
@Query("SELECT rs FROM ReservedSeat rs join fetch rs.seat WHERE rs.id LIKE :scheduleId%")
List<ReservedSeat> searchByIdStartsWith(String scheduleId);
}
왜 못 잡았는가?
- 테스트 코드를 작성하면서 영속성 콘텍스트에 데이터가 남아 있었다.
@Test
@DisplayName("스케줄 id로 좌석정보를 조회할 수 있다.")
void testFindByScheduleId() {
Ticket ticket = saveTicket();
int seatCount = 3;
IntStream.range(0, seatCount).forEach((seq) -> {
Seat seat = saveSeat(ticket.getSchedule().getTheaterRoom(), 0, seq);
ReservedSeat reservedSeat = new ReservedSeat(ticket, seat);
reservedSeatRepository.save(reservedSeat);
});
List<ResponseFindSeat> seatList = reservedSeatService.findByScheduleId(ticket.getSchedule().getId());
assertThat(seatList).hasSize(seatCount);
}
- 위 코드를 돌렸을 때 문제가 없었다.
- 하지만 중간에 service를 호출하기 전에 영속성 콘텍스트를 비워주니.. select가 날아갔다.
@Test
@DisplayName("스케줄 id로 좌석정보를 조회할 수 있다.")
void testFindByScheduleId() {
Ticket ticket = saveTicket();
int seatCount = 3;
IntStream.range(0, seatCount).forEach((seq) -> {
Seat seat = saveSeat(ticket.getSchedule().getTheaterRoom(), 0, seq);
ReservedSeat reservedSeat = new ReservedSeat(ticket, seat);
reservedSeatRepository.save(reservedSeat);
});
em.flush();
em.clear();
List<ResponseFindSeat> seatList = reservedSeatService.findByScheduleId(ticket.getSchedule().getId());
assertThat(seatList).hasSize(seatCount);
}
- service에서 find 하고, seat에 접근할 때마다 select가 날아갔다..
어떻게 잡았는가?
- 코드 리뷰하면서 팀원분께서 짚어주셨다.. 👍
앞으로는?
- 1차적으로 repository layer에서 검증을 더 꼼꼼히 잡아야겠다.
- service에서 직접 데이터를 저장하는 경우 영속성 콘텍스트를 반드시 비워두고 돌려야겠다.
반응형
'TroubleShooting > 데브코스' 카테고리의 다른 글
Service에서 DataIntegrityViolationException을 Catch 못함 (0) | 2022.07.14 |
---|---|
S3 파일 업로드 (0) | 2022.07.11 |
Service에서 다른 Service를 의존하게 된다면 ? (0) | 2022.07.06 |
Spring Data Jpa Insert 할 때 Select가 나가네.. (0) | 2022.07.01 |
JPQL like에 _ 포함시키기 (0) | 2022.07.01 |