N+1 문제를 해결하기 위해 Fetch join을 사용하다 보면 한 엔티티에서 여러 리스트를 Fetch join하는 경우가 있다. 이때 MultipleBagFetchException이 발생할 수 있다. (Bag은 JPA에서 만들어둔 중복을 허용하고 순서가 없는 컬렉션이고, 영속성 컨텍스트에 등록할 때 List를 Bag으로 포장한다고 생각하자) 아무튼 실제 SQL에서는 여러 테이블을 Fetch join하는 쿼리를 작성할 수 있는데 JPA는 왜 예외를 던질까? 간단히 말하자면 쿼리 성능에 악영향을 끼치는 카테시안 곱이 발생했음을 경고하기 위해서다. 친절한 JPA ㅋㅋㅋ.. 대응 방법 여러가지 방법이 있겠지만 공부한 내용만 나열한다. 하나의 컬렉션만 Fetch Join하기: 한 쿼리에서 여러 컬렉션을 FETC..
Tool
결론부터 말하자면 Spring Data Jpa의 JpaRepository를 사용할 때 발생하는 문제다. 처음 만난 문제는 PK 값을 가지고 있는 객체가 데이터베이스에는 존재하지 않을 때, 이 객체와 연관관계를 맺고 save 메서드로 영속화를 시도하면 연관관계를 맺고 있는 객체가 데이터베이스에 존재하지 않기 때문에 EntityNotFoundException이 발생한다. 두 번째 문제는 PK 값을 가지고 있는 객체를 JpaRepository.save 메서드로 영속화 했을 때, 이 객체는 영속화가 되지 않기 때문에 발생하는 문제다. 문제 코드 테스트 코드용 더미 데이터를 생성하는 클래스를 아래와 같이 만들었다. public class TestDataFactory { public static Long userI..
JPA에서 복합 키를 사용하려면 @Embeddable 이나 @IdClass 애너테이션을 사용해서 구현할 수 있다. 1. 공통점 Serializable 인터페이스 구현: 복합 키 클래스는 Serializable 인터페이스를 구현해야 한다. 기본 생성자: 복합 키 클래스에는 기본 생성자가 필요하다. hashCode와 equals 구현: 복합 키의 동등성을 정확히 비교하기 위해 hashCode와 equals 메소드를 오버라이드 해야 한다. 2. Embedded ID 사용하기 @Embedded를 적용한 복합 키 클래스를 정의한다. 이때, Serializable 인터페이스를 구현해야 한다. @Embeddable public class MyCompositeKey implements Serializable { pri..
조회 API에 여러 개의 동적 정렬 조건과 페이지네이션 기능이 필요할 때 Spring Data JPA로 쉽게 구현할 수 있다. 이건 단순히 게시글 목록을 조회하는 상황인데, 만약 더 복잡한 연관관계의 조회라면 이 방법은 좋지 않을 수 있으니 성능을 고려하자. Controller 페이지네이션과 동적 정렬 조건을 쿼리 스트링으로 입력받는다. 정렬 조건은 sort=key,option 형식으로 입력 받는다고 설정했다. 예를 들면 /posts&sorts=createdAt,desc 같은 형식이다. @GetMapping public ResponseEntity findAll( @RequestParam(defaultValue = "0", required = false) int page, @RequestParam(defa..
회원가입 기능을 만들 때, 시간 제한이 있는 이메일 인증을 통과하도록 만들어보자. Redis를 사용하면 TTL(Time To Live)을 써서 시간 제한을 쉽게 구현할 수 있다. 로컬에 Redis 설치하기 homebrew로 간단하게 설치했다. 설치가 끝났다면 redis 서버를 실행시키자. 서버가 실행중이라면 redis-cli ping 명령어를 입력하면 PONG이라고 응답한다. brew install redis brew services start redis redis-cli ping # PONG Spring boot 의존성 및 설정 추가하기 build.gradle의 dependencies에 redis를 추가하고, application.yml에 호스트와 포트 설정을 추가하자. // Redis 의존성 추가 i..
Bean validation에서 기본적으로 제공하는 기능 이상으로 유효성 검증을 해야한다면 커스텀 어노테이션을 만들어서 검증할 수 있다. 커스텀 어노테이션 이외에 검증하는 방법은 여러가지가 있지만, 나는 컨트롤러나 서비스 레이어에서 검증 메서드를 계속 호출하는 것이 메서드의 핵심 코드를 읽기 불편하게 만든다고 생각하기 때문에 DTO의 검증은 DTO 레이어에서 처리하는게 좋다고 생각했다. 나와 비슷한 생각이신 분들을 위해 커스텀 어노테이션과 검증 클래스로 분리해서 DTO 유효성을 검사한 방법을 공유한다. 회원가입 DTO 코드 @NoArgsConstructor @AllArgsConstructor @Getter @ValidSignUpRequest // Dto에 대한 검증은 Dto에서 처리하자 public cl..