jiisoo
지수로그
jiisoo
전체 방문자
오늘
어제

블로그 메뉴

  • home
  • Github
  • 알고리즘 문제풀이 저장소
  • 분류 전체보기 (80)
    • Java (1)
    • Spring (30)
    • JPA (15)
    • cs (8)
      • 디자인패턴 (1)
      • 네트워크 (5)
      • Database (1)
      • 운영체제 (1)
    • algorithm (18)
      • 알고리즘 & 자료구조 이론 (12)
      • 알고리즘 풀이 (6)
    • 면접 준비 (0)
    • 회고 (5)
      • ATDD (4)
      • 학습테스트로 배우는 spring 2기 (1)
      • 프로젝트 (0)

인기 글

최근 댓글

최근 글

태그

티스토리

hELLO · Designed By 정상우.
jiisoo

지수로그

[Jpa] 연관관계 매핑 기초
JPA

[Jpa] 연관관계 매핑 기초

2023. 5. 1. 19:38
  • 용어 이해
    • 방향 (Direction) : 단방향, 양방향
    • 다중성(Muitplicity) : 다대일 (N : 1), 일대다 (1 : N), 일대일 (1 : 1), 다대다 ( N : M)
    • 연관관계의 주인(Owner) : 객체 양방향 연관관계는 관리가 필요

 

💡 연관관계의 필요성

객체지향 설계의 목표는 자율적인 객체들의 협력 공통체를 만드는 것인데 객체를 테이블에 맞춰 데이터 중심으로 모델링을 하면

협력 관계를 만들 수 없기 때문이다. 

 

 

예시와 함께 살펴보자.

객체를 테이블에 맞추어 모델링 할 경우 (연관관계가 없는 객체)

  • 객체를 테이블에 맞추어 모델링한 코드
@Entity
public class Member {

  @Id @GeneratedValue
  private Long id;

  @Column(name = "USERNAME") 
  private String name;

  @Column(name = "TEAM_ID")
  private Long teamId;
  ...
  }

@Entity
public class Team {

  @Id @GeneratedValue
  private Long id;
  private String name;
  ...
  }
  • 모델링 한 객체를 가지고 DB에 저장 & 조회 하는 로직
//팀 저장
Team team = new Team();
team.setName("tistory"); 
em.persist(team);

//회원 저장
Member member = new Member();
member.setName("jisoolog");
member.setTeamId(team.getId());
em.persist(member);

 

  • 이때 회원의 팀을 찾고싶다면 어떻게 해야 할까?
Member findMember = em.find(Member.class, member.getId());
Long findTeamId = findMember.getId();
Team findTeam = em.find(Team.class, findTeamId);
  • 연관관계가 존재하지 않기 때문에, member 의 teatm을 가져와야 할 경우 
    • member 를 꺼내온 뒤, id 를 찾아 team 을 다시 조회해야 하기 때문에 
    • 회원의 team 을 가져오기 위해 많은 비용이 든다.
      • 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾고
      • 객체는 객체 참조를 사용하여 연관된 객체를 찾기 때문에 
      • 패러다임의 불일치가 발생한다.
  • 정리해보자면 
    • 객체를 테이블에 맞추어 데이터 중심으로 모델링할 경우에는 협력관계를 만들 수가 없고
    • 객체 지향스럽지 않은 코드를 작성하게 된다.

 

 

💡 단방향 연관관계 

@Entity
public class Member {

    @Id @GeneratedValue
    private Long id;

    @Column(name = "USERNAME")
    private String name;
    
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
	
    ...
}
  • 회원(Member) 객체는 @ManyToOne, @JoinColumn 을 통해서 팀(Team) 을 참조하도록 설정해주었다.
    • 회원(Member) 객체와 팀(Team)객체는 단방향 관계이기 때문에 
      • 회원(Member) 는 team 필드를 통해 팀을 알 수 있지만, 팀(Team) 객체는 소속된 member 에 대해서 알 수 없다. 
  • 테이블의 연관관계는 외래키 하나를 가지고 양측에서 서로 참조할 수 있다.

 

 

💡 양방향 연관관계

  • 양방향으로 연관관계를 맺어주면, 양측에서 서로를 참조할 수 있게 된다.
    • 단방향에서는 팀 객체에서는 소속된 member에 대해서 조회할 수 없었는데 양방향을 설정해주면 조회가 가능하게 된다.
@Entity
public class Member {

    @Id @GeneratedValue
    private Long id;

    @Column(name = "USERNAME")
    private String name;
    
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
	
    ...
}

❗️ 하지만 객체와 테이블이 관계를 맺을때의 차이점이 존재한다.

  • 객체 연관관계 = 2개
    • 회원 -> 팀 (단방향)
    • 팀 -> 회원 (단방향)
  • 테이블 연관관계 = 1개
    • 회원 <-> 팀 

때문에 객체에서 양방향 연관관계를 맺을 경우 둘 중 하나를 외래키로 관리해야 한다.

  • 두 객체가 서로를 참조하도록 값을 만들어 두었기 때문에 테이블에서 어떤 것을 외래키로 만들어 관리해야 할지 정해주어야 한다.
    • 연관관계 주인을 정해야 한다.

 

 

💡 연관관계의 주인

  • 양방향 매핑 규칙
    • 객체의 두 관계 중 하나를 연관관계의 주인으로 지정
    • 연관관계의 주인만이 외래 키를 관리(등록, 수정)
    • 주인이 아닌쪽은 읽기만 가능하다.
    • 주인은 mappedBy 속성을 사용하지 않는다.
    • 주인이 아니면 mappedBy 속성으로 주인을 지정한다.

 

❓ 이때 누구를 주인으로 정해주어야 할까?

  • 외래키가 있는 쪽을 주인으로 정해야 한다.
    • DB입장에서 외래키가 있는 쪽이 N(다) 이고 없는 쪽이 1이기 때문에
    • N 쪽이 연관관계의 주인이 되는 것.

 

❗️양방향 매핑시 가장 많이 하는 실수 

연관관계의 주인에 값을 입력하지 않는 경우 

  • 실행시켜보면 
    • 연관관계의 주인에 값을 입력하지 않을 경우 null 값이 들어오는 것을 볼 수 있다.
    • 이때 TEAM_ID 에 null 값이 들어오는 이유는 team 의 member 의 경우 주인이 아니기 때문에 
    • 읽기 전용이라 변경된 내용이 반영되지 않는다.

  • 코드를 변경하여 team이 아닌 member 에서 team 을 설정해주고 

  • 실행시켜 주면
    • 잘 반영되는 것을 확인할 수 있다.

정리해보면 🤔

  • 양방향 매핑 시 연관관계의 주인에 값을 입력해야 한다. 
  • 또한 순수한 객체 관계를 고려하면 양쪽 다 값을 입력해야 한다.
Team team = new Team();
team.setName("teamA");
em.persist(team);

Member member = new Member();
member.setName("mamber1");

team.getMembers().add(member);
member.setTeam(team);

em.persist(member);
  • 이때 team.getMembers().add(member) 를 넣지 않을 경우 문제가 발생한다.
    • DB에 반영하는데 문제는 생기지 않지만
    • 영속화 컨텍스트의 1차 캐시에 저장된 team 에서는 members 에 해당 Member가 추가되지 않은 상태이다.
    • 이때, team.members 를 사용하게 된다면 
    • DB에서 조회하는 것이 아닌 1차 캐시에서 꺼내 사용하기 때문에 해당 member 가 추가되지 않은 결과가 반환되어 문제가 발생하기 때문에 양쪽 모두에 값을 세팅해주어야 한다.
    • 연관관계 편의 메서드를 생성하여 사용하는 것이 좋다.
public void changeTeam(Team team) {
    this.team = team;
    team.getMembers().add(this);
}
  • Team 을 세팅해주는 시점에 해당 team에 Member 도 같이 추가가 된다.

 

 

 

양방향 매핑 정리 

  • 단방향 매핑을 우선으로 한 후 양방향은 필요할 때 추가해주자. (테이블에는 영향을 주지 않는다.)

 

 

 

 

 

<자바 ORM 표준 JPA 프로그래밍 - 기본 편 을 참고하여 작성하였습니다.>

'JPA' 카테고리의 다른 글

[Jpa] 다대다 [N:N] 에 대해서  (0) 2023.05.17
[Jpa] 다대일[N:1], 일대다[1:N] 에 대해서  (0) 2023.05.09
[Jpa] 엔티티 매핑  (0) 2023.04.30
[Jpa] 영속성 컨텍스트  (0) 2023.04.27
[Jpa] @OneTOOne에 대해서  (0) 2023.04.26
    'JPA' 카테고리의 다른 글
    • [Jpa] 다대다 [N:N] 에 대해서
    • [Jpa] 다대일[N:1], 일대다[1:N] 에 대해서
    • [Jpa] 엔티티 매핑
    • [Jpa] 영속성 컨텍스트
    jiisoo
    jiisoo

    티스토리툴바