[Spring] JPA 연관관계 매핑 확실하게 알아보기❗

2025. 2. 14. 17:54·Java Study/Frameworks

❔연관관계 매핑

데이터베이스에서 엔티티 간 관계를 정의하고, 이를 ORM(JPA) 을 통해 객체지향적으로 표현하는 것을 의미

 

💠연관관계 매핑이 중요한 이유

현대적인 애플리케이션에서는 여러 개의 엔티티들이 서로 연관되어 있으며, 이 관계를 올바르게 설정하는 것이 중요하다.

  • 데이터 정합성(Consistency) 유지
  • 중복 데이터 최소화 및 정규화
  • 비즈니스 로직의 직관적인 표현 가능
  • 객체 간의 관계를 코드에서 직접 표현 ➡️ 가독성과 유지보수성 향상
  • SQL JOIN을 활용한 최적화된 데이터 조회 가능

Spring Boot와 JPA를 사용하면 객체 간의 관계를 기반으로 데이터베이스 테이블 간의 관계를 매핑할 수 있다.

 


⚠️ JPA 연관관계 매핑 시 고려해야 할 점

🔸단방향 vs 양방향

구분 설명
단방향 - 한 엔티티에서만 다른 엔티티를 참조
- 외래 키를 포함한 엔티티에서만 연관된 엔티티 조회 가능
- 유지보수 용이
양방향 - 두 엔티티가 서로 참조
- 양쪽에서 엔티티 조회 가능
- 연관관계의 주인(Owner) 개념 필요
- 복잡해질 수 있음
실무에서는 가능하면 단방향을 권장하고, 정말 필요할 때만 양방향으로 설정한다.

 


 

🔸관계의 종류

1. 일대일(1:1) 관계 (@OneToOne)

한 개의 엔티티가 다른 한 개의 엔티티와 1:1로 매핑되는 관계

@Entity
public class UserInfo {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "profile_id") // 외래 키 지정
    private Profile profile;
}

@Entity
public class Profile {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String bio;

    @OneToOne(mappedBy = "profile")
    private UserInfo userInfo;
}
  • `@OneToOne` 사용
  • `@JoinColumn`을 통해 외래키 지정
  • 양방향일 경우 `mappedBy`로 연관 관계 주인 설정
  • 지연 로딩(FetchType.LAZY) 권장: 필요할 때만 가져오도록 최적화

 

2. 일대다(1:N) / 다대일(N:1) 관계 (@OneToMany, @ManyToOne)

하나의 엔티티가 여러 개의 엔티티와 연관된 경우

다대일(@ManyToOne) 관계는 외래키가 있는 쪽에서 설정한다.

일대다(@OneToMany) 관계는 단독으로 사용하면 외래키 관리가 어려워지고 성능이 저하될 수 있다.

실무에서는 가능하면 다대일(@ManyToOne)을 사용하고, 필요할 때만 일대다(@OneToMany)를 추가한다.
// ex)Team(One) ↔ Member(Many) (한 팀에는 여러 멤버가 속함)

@Entity
public class Team {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team", cascade = CascadeType.ALL)
    private List<Member> members = new ArrayList<>();
}

@Entity
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id") // 외래 키 설정
    private Team team;
}
  • `@OneToMany(mappedBy = "team")` ➡️ 외래키 관리 ❌ (연관 관계의 주인이 아님)
  • `@ManyToOne(fetch = FetchType.LAZY)` ➡️ ManyToOne 관계에서 외래키 관리 ✅
  • N:1 관계에서 외래키를 가진 쪽이 연관 관계의 주인
  • `cascade = CascadeType.ALL` 사용 시 Team 삭제 시 Member 도 삭제됨

 

3. 다대다(N:M) 관계  (@ManyToMany)

JPA에서는 잘 사용하지 않음

중간 테이블을 만들어 1:N, N:1 관계로 풀어서 사용하는 것이 일반적

 


 

🔸외래키 관리 주체

연관 관계의 주인은 외래키를 가진 테이블이 된다.

`mappedBy`를 활용하여 어느 쪽이 주인(Owner)이고, 어느 쪽이 읽기 전용인지 설정할 수 있다.

 


🔵 Cascade와 FetchType 설정

🔹Cascade (영속성 전이)

부모 엔티티에서 자식 엔티티에 대한 영속성(persistence) 전이를 설정할 수 있다.

옵션 설명
CascadeType.ALL 모든 변경 전파 (저장, 삭제 등)
CascadeType.PERSIST 저장(CREATE) 시 전파
CascadeType.MERGE 병합(UPDATE) 시 전파
CascadeType.REMOVE 삭제(DELETE) 시 전파
CascadeType.DETACH 부모 엔티티가 분리될 때 자식도 분리
Cascade.ALL은 신중하게 사용!

 

1. Cascade 사용 예시

@OneToMany(mappedBy = "team", cascade = CascadeType.ALL)
private List<Member> members = new ArrayList<>();

 

저장:

@Transactional
public void saveTeamWithMembers() {
     Team team = new Team();
     team.setName("Development Team");

     Member member1 = new Member();
     member1.setName("Alice");

     Member member2 = new Member();
     member2.setName("Bob");

     // 연관관계 설정
     team.addMember(member1);
     team.addMember(member2);

     // 팀을 저장하면 멤버도 자동 저장됨
     teamRepository.save(team);  
}
-- 내부적으로 실행되는 쿼리 
INSERT INTO team (name) VALUES ('Development Team');
INSERT INTO member (name, team_id) VALUES ('Alice', 1);
INSERT INTO member (name, team_id) VALUES ('Bob', 1);

 

삭제:

@Transactional
public void deleteTeam(Long teamId) {
    Team team = teamRepository.findById(teamId).orElseThrow();
    teamRepository.delete(team); // -> 연관된 Member 데이터도 삭제됨
}
-- 내부적으로 실행되는 쿼리
DELETE FROM member WHERE team_id = 1;
DELETE FROM team WHERE id = 1;

 

2. Cascade 권장/비권장 예시

[Cascade 권장 사용 예시]

  • @OneToMany 관계에서 자식 엔티티의 생명주기가 부모 엔티티에 종속될 때
  • @OneToOne 관계에서 하나의 부모 엔티티가 하나의 자식 엔티티를 완전히 소유하는 경우

[Cascade 비권장 사용 예시]

  • @ManyToOne 관계에서 남용할 경우 데이터 손실 위험
  • @ManyToMany 관계에서 데이터 삭제 시 복잡한 의도치 않은 삭제가 발생할 가능성이 있음

 

3. 고아 객체 제거

부모 엔티티에서 리스트를 제거하면, 해당 엔티티도 삭제되도록 설정할 수 있다.

@OneToMany(mappedBy = "team", orphanRemoval = true)
private List<Member> members = new ArrayList<>();

 


 

🔹FetchType (즉시 로딩 vs 지연 로딩)

연관된 엔티티를 조회할 때 즉시 로딩(EAGER)과 지연 로딩(LAZY) 방식이 있다.

FetchType.EAGER 즉시 로딩 (JOIN을 통해 연관된 엔티티 즉시 조회)
FetchType.LAZY 지연 로딩 (필요할 때 조회, 프록시 객체로 대체)

 

1. 즉시 로딩 (FetchType.EAGER)

  • 연관된 엔티티를 즉시 함께 조회
  • N+1 문제를 유발할 수 있음
@ManyToOne이나 @OneToOne 기본값이 EAGER이므로 LAZY로 변경하는 것이 일반적이다.

즉시 로딩(EAGER) 예제 - Member엔티티

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "team_id")
private Team team;
  • Member 를 조회하면 즉시 Team 도 함께 조회됨
  • 불필요한 데이터 조회로 성능 이슈 발생 가능

 

2. 지연 로딩 (FetchType.LAZY)

  • 연관된 엔티티를 실제로 사용할 때만 조회
  • 대부분의 경우 지연 로딩을 기본값으로 설정하는 것이 권장된다.
  • @OneToMany는 기본값이 LAZY이므로 특별한 이유가 없다면 그대로 유지해야 한다.

지연 로딩(LAZY) 예제 - Member엔티티

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
  • Member 를 조회할 때 Team 은 조회되지 않고, 필요할 때 쿼리가 실행됨
  • 권장 방식 (필요한 데이터만 로딩하여 성능 최적화)

 


🗨️ 정리

JPA 연관관계 매핑은 데이터 정합성 유지, 성능 최적화, 비즈니스 로직 직관성 측면에서 필수적인 요소이다.
단방향 매핑을 우선 적용하되, Cascade, FetchType 설정을 적절히 활용하여 유지보수성과 성능을 고려해야 한다.

저작자표시 비영리 변경금지

'Java Study > Frameworks' 카테고리의 다른 글

git clone 후 build.gradle 빨간 줄❓ 해결 방법‼️  (0) 2025.02.24
[Spring] 인증/인가와 Session 방식과 JWT 방식의 차이  (0) 2025.02.20
[Spring] @Transactional을 어디에 붙여야 할까🤔❔  (0) 2025.02.12
[Spring] spring.jpa.hibernate.ddl-auto 설정과 각 옵션  (0) 2025.02.12
[Spring] JPA 영속성 컨텍스트와 JOIN 활용  (0) 2025.02.12
'Java Study/Frameworks' 카테고리의 다른 글
  • git clone 후 build.gradle 빨간 줄❓ 해결 방법‼️
  • [Spring] 인증/인가와 Session 방식과 JWT 방식의 차이
  • [Spring] @Transactional을 어디에 붙여야 할까🤔❔
  • [Spring] spring.jpa.hibernate.ddl-auto 설정과 각 옵션
기만나🐸
기만나🐸
공부한 내용을 기록합시다 🔥🔥🔥
  • 기만나🐸
    기만나의 공부 기록 🤓
    기만나🐸
  • 전체
    오늘
    어제
    • ALL (135)
      • TIL (Today I Learned) (26)
      • Dev Projects (9)
      • Algorithm Solving (67)
        • Java (52)
        • SQL (15)
      • Java Study (24)
        • Basics (3)
        • Advanced (3)
        • Frameworks (17)
        • Etc (1)
      • Certifications (8)
        • 정보처리기사 실기 (8)
  • 인기 글

  • 태그

    백준
    greedy
    stack
    jpa
    dp
    백트래킹
    Firebase
    jQuery
    HTML
    bootstrap
    다이나믹프로그래밍
    Subquery
    자료구조
    완전탐색
    CSS
    그리디
    Google Fonts
    programmers
    BFS
    java
    mysql
    시뮬레이션
    DFS
    jwt
    BOJ
    프로그래머스
    join
    GROUP BY
    javascript
    sql
  • 최근 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.3
기만나🐸
[Spring] JPA 연관관계 매핑 확실하게 알아보기❗
상단으로

티스토리툴바