코딍코딍
코딩기록
코딍코딍
전체 방문자
오늘
어제
  • 분류 전체보기 (271)
    • 개발 (2)
    • Java (1)
    • 스프링 (28)
    • JPA (11)
    • Git (3)
    • 알고리즘 (160)
      • 백준 (132)
      • 프로그래머스 (8)
      • SWEA (20)
    • 토이 프로젝트 (14)
      • 간단한 Springboot CRUD (1)
      • 게시판 프로젝트 (13)
    • 알고리즘 개념정리 (8)
    • 오류 해결 (13)
    • 보류 (0)
    • AWS (5)
    • 트러블 슈팅 (0)
    • 회고 (3)
    • CS (4)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

최근 글

티스토리

hELLO · Designed By 정상우.
코딍코딍

코딩기록

JPA

연관관계 매핑 기초

2022. 7. 22. 18:06

객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.

  • 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다. 
  • 객체는 참조를 사용해서 연관된 객체를 찾는다.

 

 

단방향 연관관계

회원과 팀이 있을 때, 회원 -> 팀 또는 팀 -> 회원처럼 한쪽만 참조하는 것을 단방향 연관관계라고 한다. 

 

  • 객체의 참조와 테이블의 외래 키를 매핑하여 객체지향적으로 모델링 할 수 있다.
  • Member -> Team 인 단방향 연관관계

 

Member.class (Team 객체를 참조)

@Entity
public class Member { 
    @Id @GeneratedValue
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    private int age;
    
    // @Column(name = "TEAM_ID")
    // private Long teamId;
    
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    …
  • 객체 연관관계 : Member 객체의 team 필드 사용
  • 테이블 연관관계 : MEMBER 테이블의 TEAM_ID 외래 키 컬럼을 사용

Team.class

@Entity
public class Team {

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

테이블 생성 쿼리

create table Member (
     MEMBER_ID bigint not null,
     USERNAME varchar(255),
     TEAM_ID bigint,
     primary key (MEMBER_ID)
)

create table Team (
     TEAM_ID bigint not null,
     name varchar(255),
     primary key (TEAM_ID)
)

 

@ManyToOne

다대일(N:1)이라는 매핑정보이다.

 

@JoinColumn(name=" ")

조인 컬럼은 외래 키를 매핑할 때 사용한다. name 속성에 매핑할 외래 키 이름을 지정한다

 

 

양방향 연관관계

  • 객체는 참조를 통해 연관관계를 관리한다.
    • 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단 방향 관계 2개다. 
      • Member -> Team 연관관계 1개(단방향)
      • Team -> Member 연관관계 1개(단방향)
  • 테이블은 외래 키 하나로 연관관계를 관리한다.
    • 회원 <-> 팀의 연관관계 1개(양방향)
    • 사실 테이블 연관관계는 방향이란 개념이 없다. 외래 키를 통해 조인하여 무조건 양방향 연관관계기 때문이다.

 

연관관계의 주인

Member나 Team 객체 둘 중 하나로 외래키를 관리해야 한다.

양방향 매핑 규칙

  • 객체의 두 관계 중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인만이 외래 키를 관리(등록, 수정)
  • 주인이 아닌 쪽은 읽기만 가능
  • 주인은 mappedBy 속성 사용 X
  • 주인이 아니면 mappedBy 속성으로 주인 지정

 

연관관계의 주인 지정

Member의 team이 연관관계의 주인

외래 키가 있는 있는 곳을 주인으로 정한다.

 

Member.class

@Entity
public class Member {

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

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

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    ...

Team.class

@Entity
public class Team {

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

    @OneToMany(mappedBy = "team") //주인은 Member의 team
    private List<Member> members = new ArrayList<>();
    ...

 

mappedBy 속성

  • 연관관계의 주인을 정할 수 있는 속성이다. 
  • 양방향 매핑일 때 사용한다. 반대쪽 매핑의 필드 이름(Member의 team)을 값으로 주면 된다.
  • 주인은 mappedBy 속성을 사용하지 않는다.
  • 주인이 아니면 mappedBy 속성을 사용해서 속성의 값으로 연관관계의 주인을 지정해야 한다.
    • @OneToMany(mappedBy = "team") private List<Member> members = new ArrayList<>();
      • members는 team에 의해 관리가 된다.

 

양방향 연관관계 주의

1. 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자

Team team = new Team();
team.setName("TeamA");

Member member = new Member();
member.setName("member1");
member.setTeam(team); 
em.persist(member);

team.getMembers().add(member); //Team의 members에도 값 설정!
em.persist(team);

Member findMember = em.find(Member.class, member.getId()); //1차캐시에서 조회
List<Member> members = findMember.getTeam().getMembers();  //1차캐시에서 조회

for(Member m : members) {
	System.out.println("m = " + m.getName());
}
  • Member가 주인이니까 Member에만 값을 입력한 경우엔 트랜잭션이 끝나기 전에 만약 Team에서 members를 꺼낼 때 1차 캐시에서 조회하므로 members에는 값이 없어서 members를 조회하면 문제가 된다. 고로 Team에도 값 설정을 해주자.

2. 연관관계 편의 메서드를 생성하자

Member.class에 changeTeam() 추가

//연관관계 편의 메서드
public void changeTeam(Team team) {
	this.team = team;
	team.getMembers().add(this); //주인이 아닌 반대편 매핑 필드에도 값 설정
}
  • Team의 members에도 값 설정하는 연관관계  편의 메소드 생성

Team.class에 addMember() 추가

//연관관계 편의 메서드
public void addMember(Member member) {
    members.add(member);
    member.setTeam(this);
}

JpaMain.class

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("member1");
em.persist(member);

member.changeTeam(team);
//team.addMember(member);

Member findMember = em.find(Member.class, member.getId()); //1차캐시에서 조회
List<Member> members = findMember.getTeam().getMembers();  //1차캐시에서 조회

System.out.println("=======================");
for(Member m : members) {
	System.out.println("m = " + m.getName());
}
System.out.println("=======================");
  • chageTeam()이나 addMember() 중 한 가지를 선택하여 사용하면 된다. 위 코드에서는 changeTeam()을 사용했다.

 

 

3. 양방향 매핑 시에 무한 루프를 조심하자

예: toString(), lombok, JSON 생성 라이브러리

 

 

양방향 매핑 정리

  • 단방향 매핑만으로도  연관관계 매핑은 완료하자!
  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것뿐이다.
  • 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 된다. (테이블에 영향을 주지 않기 때문)
  • JPQL에서 역방향으로 탐색할 일이 많음

 

'JPA' 카테고리의 다른 글

고급 매핑  (0) 2022.07.26
다양한 연관관계 매핑  (0) 2022.07.25
엔티티 매핑  (0) 2022.07.21
영속성 관리  (1) 2022.07.20
JPA란?  (0) 2022.07.20
    'JPA' 카테고리의 다른 글
    • 고급 매핑
    • 다양한 연관관계 매핑
    • 엔티티 매핑
    • 영속성 관리
    코딍코딍
    코딍코딍
    ㅎ2

    티스토리툴바