일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 접근지시자
- System.out
- 바운디드 타입
- auto.create.topics.enable
- 자바할래
- 항해99
- Switch Expressions
- github api
- throwable
- 제네릭 와일드 카드
- 상속
- 스파르타코딩클럽
- Study Halle
- junit 5
- annotation processor
- 자바스터디
- yield
- raw 타입
- 프리미티브 타입
- docker
- System.err
- 익명 클래스
- 로컬 클래스
- 정렬
- System.in
- 브릿지 메소드
- 제네릭 타입
- 람다식
- 함수형 인터페이스
- 합병 정렬
- Today
- Total
코딩하는 털보
21.11.01 TIL 본문
오늘의 삽질
org.hibernate.LazyInitializationException
User 엔티티는 Habit 엔티티와 다대일 양방향 매핑되어있다.
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Habit> habit;
그런데 아래 비즈니스 로직에서 for문을 돌때 org.hibernate.LazyInitializationException 예외가 발생한다.
@Override
public List<HabitSummaryVo> getHabitSummaryList(User user) {
List<HabitSummaryVo> habitSummaryList = new ArrayList<>();
List<Habit> habits = user.getHabit();
for (Habit habit : habits) {
habitSummaryList.add(HabitSummaryVo.of((HabitWithCounter) habit));
}
return habitSummaryList;
}
예외 이름만 봐도 @OneToMany는 fetchmode Lazy 가 디폴트이므로 발생하는 문제라는 것은 알 수 있었다.
Lazy는 처음에는 프록시 객체를 생성하여 넣어놓지만 실제로 그 객체를 사용하려고 할 때 조회쿼리가 자동으로 나간다고 알 고 있었는데..
여기서는 왜 문제가 발생하는 것일까?
결과부터 말하자면 트랜잭션 밖에서 조회했기 때문이다.
User 엔티티는 UserDetails 구현 서비스에서 아래와 같이 DB에서 꺼내온다.
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
User user = userRepository.findBySocialId(userId)
.orElseThrow(() -> new UsernameNotFoundException(userId + "를 찾을 수 없습니다."));
return new UserDetailsImpl(user);
그 이후에 HandlerMapping과 컨트롤러를 거쳐서 위의 서비스 로직까지 도달하게 된다.
예상에는 그 사이에 트랜잭션이 계속 유지되지 않는 것으로 생각된다.
user 엔티티의 ID를 조건으로하여 habit 테이블에서 찾아오는 쿼리를 한번 더 날리면 문제는 해결된다.
@Override
public List<HabitSummaryVo> getHabitSummaryList(User user) {
List<HabitSummaryVo> habitSummaryList = new ArrayList<>();
List<Habit> habits = habitWithCounterRepository.findByUser(user);
for (Habit habit : habits) {
habitSummaryList.add(HabitSummaryVo.of((HabitWithCounter) habit));
}
return habitSummaryList;
}
org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing
분명히 요청에는 Body를 Json 타입으로 넣어서 보내주는데, 바디가 없다는 에러가 발생한다.
@ApiOperation(value = "몬스터 변경", notes = "변경된 몬스터 데이터 반환")
@PatchMapping("/user/monster")
public MonsterResponseDto updateMonster(
@ApiIgnore @AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestBody MonsterSelectRequestDto requestDto) {
return monsterService.updateMonster(userDetails.getUser(), requestDto);
}
Http Method도 바꿔보고 Content Type 도 바꿔보고 이것저것 해보다가
검색으로 원인을 파악할 수 있었다.
원인은 filter에 있었던 getInputStream() 때문이었다.
정확히는 HttpServletRequest의 InputStream은 한 번 읽으면 다시 읽을 수 없기 때문에
@RequestBody 어노테이션이 제대로 request를 읽을 수 없었던 것.
ServletInputStream inputStream = request.getInputStream();
filter에 있던 getInputStream() 메서드는 토큰 오류시 요청 바디를 다시 넘겨주기 위해서 사용하고 있었으므로
오류가 발생할 때만 getInputStream() 메서드를 사용하도록 분기했다.