연관관계 매핑 기초

inho ha·2022년 5월 12일
0

자바 ORM 표준 JPA 프로그래밍

http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788960777330

객체와 테이블

객체는 참조(주소)를 사용해서 관계를 맺는다.
테이블은 외래 키를 사용해서 관계를 맺는다.

JPA를 사용해서 객체의 참조와 테이블의 외래 키를 매핑해야 한다!!!!

방향 :
단방향 양방향이 있다.
테이블은 항상 양방향이고, 객체는 단방향도 가능하다.

객체를 양방향으로 만들면 연관관계의 주인을 정해야한다.

단방향 연관관계

팀과 멤버가 있는 경우

객체에서 멤버는 getTeam()으로 팀을 알수있지만 팀은 멤버를 알 수 없는 단방향 관계다.
이처럼 객체가 참조를 사용해서 연관관계를 탐색하는 것이 객체 그래프 탐색이다.

테이블에서 멤버는 외래 키로 멤버와 팀을 조인할 수 있고 팀도 기본 키로 팀과 멤버를 조인할 수 있는 양방향 관계다.

객체에서 팀이 멤버를 알게하기 위해서는 팀에 멤버 필드를 추가해서 참조를 보관해야한다.
이것은 양방향 관계는 아니고 서로 다른 단방향 관계 2개이다.

JPA로 객체 관계 매핑

@ManyToOne :
연관관계를 매핑할 때 둘의 관계를 나타내는 어노테이션이다.
optional 속성이 false면 연관된 엔티티가 항상 있어야한다.
기본값은 true이다.
fetch 속성으로 LAZY 로딩과 EAGER 로딩을 설정할 수 있다.
cascade 속성으로 영속성 전이 기능을 사용할 수 있다.
targetEntity 속성으로 연관된 엔티티의 타입 정보를 설정한다.

@JoinColumn :
외래키를 매핑할 때 사용한다.
name 속성으로 매핑할 외래 키 이름을 지정한다.
생략하면 외래 키를 찾을 때 '필드명_참조하는 테이블의 컬럼명'으로 외래 키 이름을 지정한다.

연관관계 조회

연관관계가 있는 엔티티를 조회하는 방법

1. 객체 그래프 탐색

member.getTeam(); 이처럼 객체를 통해 연관된 엔티티를 조회하는 것이다.

2. 객체지향 쿼리 사용(JPQL)

String jpql = "select m from Member m join m.team t where " + "t.name=:teamName";

List<Member> resultList = em.createQuery(jpql, Member.class)
	.setParameter("teamName", "팀1")
    .getResultList();

이렇게 팀에 속한 멤버들을 조회할 수 있다.

연관관계 저장, 수정, 삭제는
member.setTeam();
em.persist(member);
로 저장
member.setTeam();
으로 수정
member.setTeam(null);
로 삭제할 수 있다.

양방향 연관관계

테이블은 외래키 하나만으로 양방향 조회가 가능하다.
객체는 외래 키가 없는 쪽에 참조 필드를 추가해줘야한다.

참조 필드는 mappedBy 속성에 반대쪽 매핑 필드 이름을 값으로 주면 된다.

이렇게 설정 해주면 team.getMembers()로 팀의 멤버들을 조회할 수 있다.

연관관계의 주인

객체에는 양방향 연관관계가 없고 단방향 연관관계가 2개인 것이다.
테이블은 외래 키가 하나인데 객체는 참조가 둘이다.
이런 차이 때문에 JPA에서는 두 객체 연관관계 중 하나를 정해서 테이블의 외래 키를 관리해야한다.
연관관계의 주인만이 데이터베이스의 연관관계와 매핑되고 외래키를 관리할 수 있다.
주인이 아닌 쪽은 읽기만 할 수 있다.
어떤 연관관계를 주인으로 정할지는 mappedBy 속성을 사용하면 된다.
주인은 mappedBy 속성을 사용하지 않는다.

연관관계의 주인을 정하는 것은 외래 키 관리자를 선택하는 것이다.
team_id 외래 키는 회원 테이블에 있다.
이를 관리하려면 회원 엔티티에 있는 member.team에서 관리를 하면 자기 테이블에 있는 외래 키를 관리하게된다.
팀 엔티티에 있는 team.members가 관리하게 되면 다른 테이블에 있는 외래 키를 관리하게 된다.
같은 테이블에 있는 키를 관리하는 것이 더 편리하므로 연관관계의 주인은 외래 키가 있는 곳으로 설정해야한다.

따라서 member.team이 연관관계의 주인이 되고
Team.members에는 mappedBy="team"으로 member.team이 연관관계의 주인임을 나타내주면된다.

다대일에서는 다 쪽이 항상 외래키를 가지기 때문에 다 쪽이 연관관계의 주인이다.
그래서 @ManyToOne에는 mappedBy 속성이 없다.

양방향 연관관계의 주의점

양방향 연관관계에서 주인이 아닌 곳에만 값을 입력하면 외래키 값이 정상적으로 저장되지 않는다.

연관관계의 주인인 곳에서만 값을 추가해주면 외래 키 값이 정상적으로 저장된다.

하지만 양쪽 모두에서 저장해줘야한다.
ORM은 객체와 관계형 데이터베이스 둘다 중요하기 때문이다.
팀에서도 team.getMembers().add(member1); 으로 연관관계를 추가해줘야한다.

이를 누락하는 실수를 방지하기 위해 setTeam() 메소드를 수정해주면 좋다.
member의 메서드 setTeam에서 team을 인자로 받아서 member.team 도 설정해주고 team.members 에도 member를 추가해주도록 하면 된다.

여기서 끝나면 안된다!!!!!!!

멤버의 팀이 이미 설정된 상태에서 다른 팀으로 수정하면 기존의 팀에서 getMembers를 호출하면 멤버가 여전히 나오는 버그가 생길 수 있다.

따라서 기존의 팀이 null이 아니면 해당 팀에서 멤버 삭제를 먼저 해주도록 코드를 추가해야한다.

무한 루프 조심

Member.toString() 에서 getTeam()을 호출하고 Team.toString()에서 getMember()를 호출하면 무한 루프에 빠질 수 있다.
엔티티를 JSON으로 변환할 때 자주 발생한다.
Lombok 을 사용할 때도 자주 발생한다.

profile
iha / ian / inho ha

0개의 댓글