JPA & QueryDSL

[JPA] 연관관계 매핑 - @ManyToOne, @OneToMany

2023. 10. 6. 14:08
목차
  1. JPA 연관관계 매핑의 종류
  2. @ManyToOne(다대일 매핑)
  3. 다대일 양방향 매핑 - @OneToMany(mappedBy = "xxx")
  4. 편의 메서드 생성
  5. 결론
  6. @OneToMany(일대다 매핑)
  7. 일대다 단방향 매핑의 단점
  8. 일대다 양방향 매핑 (공식적 x)

JPA 연관관계 매핑의 종류


JPA 연관관계 매핑의 종류는 @ManyToOne, @OneToMany, @OneToOne, @ManyToMany가 있다.

이 글에서는 @ManyToOne과 @OneToMany 관계에 대해 다룬다.

 

아래 예제에서는 Team에 속해있는 여러 Member들을 가정했다.

 

@ManyToOne(다대일 매핑)


가장 많이 사용하는 연관관계로 여러 개(Many)의 멤버가 한 개(One)의 팀에 속해있는 것을 생각하면 된다.

Member가 Team의 ID를 FK로 갖게 된다.

이러한 관계에서 연관관계의 주인은 Member이다.

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    
    private String username;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}
@Entity
public class Team {
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    
    private String name;
}

이렇게 설계하면 Member.getTeam()으로 멤버가 속해있는 팀의 정보를 불러다 사용할 수 있고,

이는 내부적으로 Team의 TEAM_ID 칼럼이 Member의 FK로 등록되게 된다.  

ManyToOne 단방향 매핑

Member 객체는 Team team을 필드로 갖지만, MEMBER 테이블에서는 TEAM_ID를 칼럼으로 갖게 된다.

@ManyToOne -> @JoinColumn(name = "TEAM_ID")로 지정해서 TEAM의 TEAM_ID 칼럼과 매핑할 수 있는 것이다.

 

여기까지가 기본적인 매핑이다. 그러나 편의를 위해 Team 객체 쪽에서도 Team에 속한 멤버의 리스트를 조회할 수 있도록 할 수 있다. 


다대일 양방향 매핑 - @OneToMany(mappedBy = "xxx")

@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    
    private String name;

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

아래와 같이 @OneToMany 어노테이션을 통해 반대로 Team 쪽에서 members 리스트를 필드로 가질 수 있다.

(mappedBy = "team") 속성은 꼭 추가해야 한다. Member의 "team" 필드에 의해 매핑되었다는 의미이다.

List<Member> memberList = teamA.getMembers();

위와 같이 Team 객체에서 getter 메서드로 members 리스트를 불러다 사용할 수 있다.

 

그러나 이것은 테이블과는 아무런 관련이 없는 기능이다.

이 members라는 필드는 자바 코드에서만 편의를 위해 사용하려고 만드는 부가적인 기능일 뿐이다.

Member member = new Member();
teamA.getMembers().add(member);

위와 같이 teamA에서 getMembers()로 teamA에 속한 memberList를 불러와서 그 list에 새로운 멤버를 추가해도

JPA 영속성 컨텍스트와 DB의 테이블에는 아무런 영향도 생기지 않는다.

즉, 이 members 필드는 오직 조회만을 위한 필드이다.

여전히 해당 팀에 속한 멤버를 추가하려면 멤버를 만들고, member.setTeam(teamA) 로 추가해야 한다.

ManyToOne 양방향 매핑

위의 그림과 같이 객체의 연관관계에서는 Team.members의 OneToMany 연관관계가 추가되었지만,테이블 상에서는 아무런 변화가 없는 것을 알 수 있다.  그러나 또 다른 문제가 있다.JPA의 쓰기 지연 때문에 memberC.setTeam(teamA)를 실행해도 해당 트랜잭션이 커밋되기 전까지는

teamA.members에 memberC가 포함되지 않을 수 있다.

따라서 @OneToMany(mapped = "team")으로 멤버 리스트를 사용하려 할 때는 추가적으로 편의 메서드를 생성하여 이 부분까지 관리하는 것이 좋다.


편의 메서드 생성

@Entity
public class Member {
    //생략
    public void changeTeam(Team team){
        this.team = team;
        team.getMembers().add(this);
    }
}

위와 같이 테이블과는 아무 관련이 없지만 team.getMembers(). add(this) 코드를 추가함으로써 예기치 못한 오류를 방지할 수 있다. 여기서 this는 Member 객체 자신을 나타낸다.

( 기본 setter인 setTeam과의 구분을 위해 메서드 명을 changeTeam으로 설정했다. )

결론

@ManyToOne은 가장 많이 사용하는 기본적인 매핑 방법이고, 가장 권장되는 방법이다.

@ManyToOne 매핑 시에 추가로 @OneToMany(mappedBy = "xxx")을 통해 양방향 매핑으로 설정할 수 있지만, 이 부분은 항상 사용하길 권장되진 않는다.

꼭 필요할 때만 사용하면 좋을 것 같다. (Team에 속한 멤버 리스트 데이터가 자주 필요한 경우 등)

 


 

@OneToMany(일대다 매핑)


Team과 Member 사이의 OneToMany 매핑도 사실 현실에서 생각해 보면 똑같은 관계이다.

하나(One)의  Team에 여러 명(Many)의 Member가 속해있는 것이다.

Table에서도 똑같다. 여러 Member는 TEAM_ID를 FK로 가지게 된다.

 

그러나 객체 세상에서만 조금 다르다.

Team의 members 필드에서 Member를 관리하고, Member 객체에서는 Team을 알 수 없다.

@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<>();
}
@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    private String username;
}

이렇게 설계하면 Member 객체에서는 자신이 속한 Team의 정보를 알 수 없고, Team에서만 Member 리스트를 알 수 있다.

이 매핑에서 연관관계의 주인은 Team이 된다.

 

테이블에서 보자면 Team.getMembers(). addMember(memberA);를 실행했을 때,

Team 테이블에는 변화가 없고 MEMBER의 TEAM_ID를 UPDATE 하는 쿼리가 날아간다.

 

OneToMany 단방향 매핑


일대다 단방향 매핑의 단점

이 경우에는 Member객체에는 TEAM관련 속성이 없지만, MEMBER 테이블에는 TEAM_ID(FK)가 존재하게 된다.

즉, 엔티티가 관리하는 FK가 다른 테이블에 존재하게 된다.

또한 Team을 건드렸는데 Member에 대한 UPDATE SQL 나간다.

뭔가 자연스럽지 못하고 객체와 테이블의 불일치가 존재한다.

 

따라서 이 방법은 권장하지 않는 방법이다.

특별한 이유가 없다면 Member에 @ManyToOne으로 Team 필드를 갖게 하는 방식을 사용하는 게 좋을 것 같다.

그리고 Team에서 members가 필요하더라도 ManyToOne 양방향 매핑을 사용하는 게 더 나은 방법이다.


일대다 양방향 매핑 (공식적 x)

이 방법은 공식적으로 제공하는 방식이 아니다. 꼭 필요하지 않으면 사용하지 않는 것을 권장한다.

@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    private String username;
    
    @ManyToOne
    @JoinColumn(name = "TEAM_ID", insertable = false, updatable = false) //읽기 전용으로 설정
    private Team team;
}

Member 객체에 Team 필드를 추가하여 ManyToOne 연관관계의 주인처럼 만들고,

JoinColumn에서 쓰기 기능을 사용하지 못하게 만든다. ( insertable = false, updatable = false )

이렇게 하면 Member에서 Team을 조회하는 기능을 추가할 수 있다.

 

일대다 양방향 매핑

이 방법은 딱 봐도 이유 없이 복잡해지고 억지스러운 부분이 많다.

다대일 단방향을 이용하고, Team에서 members가 필요하다면 다대일 양방향 매핑을 이용하자! 

 


(참고) 인프런 - 김영한 님 '자바 ORM 표준 JPA 프로그래밍 - 기본 편'
https://www.inflearn.com/course/ORM-JPA-Basic
저작자표시 (새창열림)

'JPA & QueryDSL' 카테고리의 다른 글

[JPA] 연관관계 매핑  (1) 2023.10.06
[JPA] 연관관계 매핑 - @OneToOne 일대일 매핑  (0) 2023.10.06
[JPA] @Entity - 필드와 컬럼 매핑의 여러가지 속성  (1) 2023.10.04
[JPA] @Entity - 기본 키 매핑  (1) 2023.10.04
[JPA] 엔티티 매핑  (0) 2023.10.04
  1. JPA 연관관계 매핑의 종류
  2. @ManyToOne(다대일 매핑)
  3. 다대일 양방향 매핑 - @OneToMany(mappedBy = "xxx")
  4. 편의 메서드 생성
  5. 결론
  6. @OneToMany(일대다 매핑)
  7. 일대다 단방향 매핑의 단점
  8. 일대다 양방향 매핑 (공식적 x)
'JPA & QueryDSL' 카테고리의 다른 글
  • [JPA] 연관관계 매핑
  • [JPA] 연관관계 매핑 - @OneToOne 일대일 매핑
  • [JPA] @Entity - 필드와 컬럼 매핑의 여러가지 속성
  • [JPA] @Entity - 기본 키 매핑
HSRyuuu
HSRyuuu
Web Backend Developer happyhsryu
HSRyuuu
HS_dev_log
HSRyuuu
전체
오늘
어제
  • 전체 글 보기 (235)
    • Java (25)
    • Spring (29)
    • JPA & QueryDSL (13)
    • Database (17)
    • 자료구조 & 알고리즘 (30)
    • DevOps (10)
    • [ Computer Science ] (47)
      • Web & Network (14)
      • 프로그래밍 이론 (11)
      • 운영체제 (3)
      • 데이터베이스 이론 (5)
      • Linux 리눅스 (7)
    • [ Frontend ] (17)
      • Vue.js & Nuxt.js (9)
      • JSP_Thymeleaf (7)
    • [ 기타 ] (47)
      • 오픈소스 라이브러리 (5)
      • 코딩테스트 (13)
      • Trouble Shooting (7)
      • Tech Interview (6)
      • Book Review (9)
      • 끄적끄적... (5)
      • 개인 프로젝트 (2)

블로그 메뉴

  • 홈
  • 태그
  • github

공지사항

  • GitHub
  • 공부한 내용을 정리하고 기록하는 블로그 입니다.

인기 글

태그

  • Database
  • 개발자
  • 백엔드
  • SpringBoot
  • springsecurity
  • MySQL
  • 기술면접
  • 자료구조
  • Redis
  • Nuxt3
  • 백엔드공부
  • 트랜잭션
  • 리눅스
  • 백엔드기술면접
  • cleancode
  • Java
  • JPA
  • SQL
  • Redisson
  • TechInterview
  • HTTP
  • mybatis
  • Linux
  • web
  • 클린코드
  • 백엔드스쿨
  • vue3
  • Spring
  • 백준
  • 제로베이스

최근 댓글

최근 글

hELLO · Designed By 정상우.
HSRyuuu
[JPA] 연관관계 매핑 - @ManyToOne, @OneToMany
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.