TIL - 20250730

juni·2025년 7월 30일

TIL

목록 보기
79/317

0730 (JPA N+1 문제, Fetch Join, Cascade, 다대다 연관관계, SQL 서브쿼리)

1. JPA 양방향 연관관계에서의 편의 메서드

  • changeDepartment: Employee에서 부서를 바꾸는 편의 메서드로, 반대편 관계인 Department.employees에도 추가.
  • addEmployee: Department에서 사원을 추가할 때 EmployeesetDepartment(this)까지 같이 수행하여 양방향 일관성을 유지.
public void changeDepartment(Department department) {
    this.department = department;
    if (department != null) {
        department.getEmployees().add(this);
    }
}

public void addEmployee(Employee employee) {
    employee.setDepartment(this);
    this.employees.add(employee);
}

2. Fetch 전략과 N+1 문제

N+1 문제란?

  • 1개의 쿼리(N)를 실행하고 각 결과마다 추가 쿼리(1)를 반복 실행하는 비효율적 상황.

해결 방법

  • @OneToMany(fetch = FetchType.LAZY) 설정을 통해 지연 로딩하거나, 명시적으로 Fetch Join 사용.
@Query("SELECT d FROM Department d JOIN FETCH d.employees")
List<Department> findAllByFetch();

3. Cascade와 orphanRemoval

  • cascade = CascadeType.ALL: 연관 엔티티의 저장, 삭제 등의 작업이 자동 전파됨.
  • orphanRemoval = true: 관계에서 제거된 엔티티는 DB에서도 자동 삭제됨.
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Employee> employees;

4. ManyToMany 관계 해소: 중간 엔티티

중간 엔티티 Purchase

  • UserGoods 는 직접 다대다 연결하지 않고 중간 테이블 Purchase 를 사용해 1\:N, N:1 구조로 해소.
@Entity
public class Purchase {
    @ManyToOne(fetch = FetchType.LAZY)
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    private Goods goods;
}

5. Cascade 활용 예시

  • 유저 삭제 시 구매 기록도 같이 삭제됨 (CascadeType.ALL, orphanRemoval = true 적용)
userRepository.delete(user);

6. SQL 서브쿼리 정리

인라인 뷰 (FROM절 서브쿼리)

SELECT U.USER_ID, U.USERNAME, PC.POST_COUNT
FROM (
  SELECT USER_ID, COUNT(*) AS POST_COUNT
  FROM POSTS
  GROUP BY USER_ID
) PC
JOIN USERS U ON PC.USER_ID = U.USER_ID;

스칼라 서브쿼리 (SELECT절 내부에서 1개의 결과를 가져옴)

SELECT U.USER_ID, U.USERNAME,
  (SELECT BIO FROM USER_PROFILES UP WHERE U.USER_ID = UP.USER_ID) AS BIO
FROM USERS U;

연관 서브쿼리 예시

  • 게시글 좋아요 수, 댓글 수를 한 번에 가져오기
SELECT
  P.POST_ID,
  P.CONTENT,
  (SELECT COUNT(*) FROM LIKES L WHERE L.POST_ID = P.POST_ID) AS LIKE_COUNT,
  (SELECT COUNT(*) FROM COMMENTS C WHERE C.POST_ID = P.POST_ID) AS REPLY_COUNT
FROM POSTS P;

7. EXISTS 서브쿼리

게시물을 작성한 유저 조회

SELECT U.USER_ID, U.USERNAME
FROM USERS U
WHERE EXISTS (
  SELECT 1 FROM POSTS P WHERE P.USER_ID = U.USER_ID
);

게시물을 작성하지 않은 유저 조회

SELECT U.USER_ID, U.USERNAME
FROM USERS U
WHERE NOT EXISTS (
  SELECT 1 FROM POSTS P WHERE P.USER_ID = U.USER_ID
);

✅ 요약

  • JPA에서 양방향 연관관계 시 편의 메서드를 작성하여 일관성 유지.
  • N+1 문제는 Fetch Join으로 해결.
  • ManyToMany는 중간 엔티티(Purchase)로 해결.
  • Cascade 옵션을 통해 연관 엔티티 자동 삭제.
  • SQL에서는 인라인뷰, 스칼라, 연관 서브쿼리, EXISTS 등 다양한 서브쿼리를 통해 복잡한 조회를 효율적으로 처리 가능.

0개의 댓글