Join Fetch 특징
- 일반적인 join과 달리 지연 로딩(Lazy Loading)을 방지하여 연관된 엔티티를 즉시 로딩(Eager Loading)하게 함
- 기본적으로 Inner Join
- LEFT JOIN FETCH로 Left Outer Join은 가능
- JPQL은 RIGHT JOIN과 FULL JOIN을 지원하지 않음
- N + 1 쿼리 문제 방지하는데 유용하고 성능 최적화에 도움
- 연관된 엔티티를 한 번의 쿼리로 모두 조회하여 데이터베이스 쿼리 횟수 줄임
주의점
- 메모리 사용량이 많아질 수 있어서 모든 경우에 적합하지는 않음
- 쿼리의 수를 줄이는 데 도움이 되지만, 연관된 엔티티가 많을 경우 한 번에 많은 데이터가 로드 될 수 있기 때문
- 복잡한 쿼리
- 여러 엔티티를 Join Fetch로 함께 조회할 때 조인되는 테이블의 데이터가 많으면 중복된 데이터 반환될 수 있음
- 이런 경우에 결과를 적절히 처리하는 방법을 고려해야 함
Board 클래스와 User 클래스의 연관 관계
[Board 클래스 - 엔티티]
- Board와 User 객체는 n:1 관계
- 참조하고있는 User 객체에 @ManyToOne(fetch = FetchType.LAZY) 설정
@NoArgsConstructor(access = AccessLevel.PROTECTED) // 외부에서 빈 생성자 생성하지 못하게 제한(protected)
@Getter
@Table(name = "board_tb") // 테이블 명
@Entity // Board 객체를 테이블로 생성하며, 데이터베이스 테이블과 매핑하여 데이터베이스 작업(insert, update, delete 등)을 수행
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // AutoIncrement
private Integer id;
private String title;
private String content;
// 연관 관계 설정해놓은 User 객체(user_tb 테이블) 자체를 들고오는 것이 아니라
// user_tb 테이블의 PK (user_id(필드로는 userId))를 들고옴
// 연관 관계 설정 (User 쪽이 1이니까 ManyToOne)
@ManyToOne(fetch = FetchType.LAZY)
private User user;
// update(글 수정)를 setter로 만들어서 활용하기로 함
// setter는 명확한 의도가 있는것만 해놓는게 좋기 때문에
public void update(String title, String content) {
this.title = title;
this.content = content;
}
}[BoardRepository 클래스]
- Board와 User 객체는 n:1 관계
- id를 통해 Board 객체 1개 찾는 메서드 생성
- JPQL 쿼리를 사용한 Join
- Join 결과가 하나의 테이블이 됨
- fetch가 빠지면 user 테이블이 null이라서 콘솔 출력 안됨
@RequiredArgsConstructor
@Repository
public class BoardRepository {
private final EntityManager em;
// id를 통해 Board 객체 1개 찾는 메서드
public Optional<Board> findByIdJoinUser(int id) {
// 조인 결과가 하나의 테이블 / JPQL쿼리 사용
// fetch가 빠지면 user테이블이 null이라 콘솔 안나옴
String sql = """
select b from Board b join fetch b.user where b.id = :id
""";
Query q = em.createQuery(sql, Board.class);
q.setParameter("id", id);
try { // 해당 try문은 이상한 구조로 실제 적용X. 참고만 하기
Board board = (Board) q.getSingleResult();
return Optional.ofNullable(board);
} catch (RuntimeException e) {
return Optional.ofNullable(null);
}
}
}연관 관계(ORM)에 있는 객체(Board, User)를 1번에 불러올 경우의 장점
성능 향상
- 지연 로딩 방지
- fetch = FetchType.LAZY(지연 로딩)로 설정된 관계에서는 User 객체를 사용할 때 추가 쿼리 실행됨
- join fetch 사용하면 Board 객체 로드할 때 User 객체도 함께 로드돼서 추가 쿼리 방지 할 수 있음
- 쿼리 최적화
- 한 번의 쿼리로 Board와 User 데이터를 모두 가져오기 때문에 데이터베이스와의 통신 횟수 줄일 수 있음
- 데이터베이스 부하를 줄이고 네트워크 트래픽 감소시켜서 성능 최적화
코드의 간결성
- 명확한 데이터 접근
- Board와 관련된 User 정보를 별도의 쿼리 없이 즉시 사용할 수 있어서 코드가 간결해짐
- 복잡성 감소
- 여러 번의 쿼리를 수행할 필요가 없어서 코드의 복잡성이 줄어듦
일관된 데이터
- 데이터 일관성 유지
- join fetch 사용해서 연관된 엔티티를 한 번에 로드하면 동일한 트랜잭션 내에서 일관된 데이터 사용 가능
- 데이터베이스 상태의 일관성을 유지하는 데 도움됨
Share article