[자바 ORM 표준 JPA 프로그래밍] 5. 연관관계 매핑 기초
2022. 5. 13. 01:54ㆍWeb/Spring
반응형
velog에서 옮겨온 글 입니다.
원본 날짜: 2022-03-16T10:55:24.003Z
김영한님의 '자바 ORM 표준 JPA 프로그래밍' 서적을 읽고 정리한 글 입니다.
핵심 키워드
- 방향(Direction)
- 단방향, 양방향
- 다중성(Multiplicity)
- 다대일, 일대다, 일대일, 다대다
- 연관 관계의 주인(Owner)
- 객체 양방향 연관 관계는 관리하는 주인이 필요하다.
단방향 연관관계
@ManyToOne
@JoinColumn(name = "TEAM_ID") // join 하기 위한 FK
private Team team;
@JoinColumn
을 생략하면 외래 키를 찾을 때 기본 전략을 사용한다. (기본 전략:필드명 + _ + 참조하는 테이블의 컬럼명
)
// 팀 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
// 회원1 저장
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1);
em.persist(member1);
// 회원2 저장
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1);
em.persist(member2);
JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 한다.
연관된 엔티티 삭제
member1.setTeam(null);
member2.setTeam(null);
em.remove(team1);
- 연관된 엔티티를 삭제하려면 기존에 있던 연관관계를 먼저 제고하고 삭제해야 한다.
- 그렇지 않으면 외래 키 제약조건으로 인해, DB에서 오류가 발생한다.
객체 연관관계 vs 테이블 연관관계
- 객체는 참조(주소)로 연관관계를 맺는다.
- 테이블은 외래 키로 연관관계를 맺는다.
- 참조를 사용하는 객체의 연관관계는 단방향이다.
- 외래 키를 사용하는 테이블의 연관관계는 양방향이다.
- 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 한다.
양방향 연관관계
- 양방향으로 한다고 해도 테이블 연관 관계는 변한 것이 없다.
- 문제는 객체다.
Team
에Member
를 넣어줘야만 접근이 가능하다.
@OneToMany(mappedBy = "team") // Member 엔티티의 team 변수와 연결되어 있음을 의미
List<Member> members = new ArrayList<Member>();
연관관계의 주인
- 엔티티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데 외래키는 하나다. 따라서 둘 사이에 차이가 발생한다.
- 그래서 두 객체 연관관계 중 하나를 정해서 테이블의 외래 키를 관리해야 하는데 이것을 연관관계의 주인(Owner)이라 한다.
- 주인이 아니면
mappedBy
속성을 사용해서 속성의 값으로 주인을 지정해야 한다.
연관관계의 주인만이 DB 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)할 수 있다. 주인이 아닌 쪽은 읽기만 할 수 있다. 즉, 연관관계의 주인을 정한다는 것은 사실 외래 키 관리자를 선택하는 것이다.
DB 테이블의 다대일, 일대다 관계에서는 항상 다 쪽이 외래 키를 가진다. 따라서
@ManyToOne
에는mappedBy
속성이 없다.
양방향 연관관계의 주의점
// 회원1 저장
Member member1 = new Member("member1", "회원1");
em.persist(member1);
// 회원2 저장
Member member2 = new Member("member2", "회원2");
em.persist(member2);
Team team1 = new Team("team1", "팀1");
// 주인이 아닌 곳만 연관관계 설정
team1.getMembers().add(member1);
team1.getMembers().add(member2);
em.persist(team1);
- 회원을 조회한 결과는 두 회원 모두
TEAM_ID
가null
값이 입력되어 있다. - 왜냐하면 연관관계의 주인이 아닌
Team.members
에만 값을 저장했기 때문이다.
객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전하다.
연관관계 편의 메소드
- 양쪽 방향 모두 값을 입력하는 메소드를 각각 호출하다 보면 실수로 하나만 호출해서 양방향이 깨질 수 있다.
- 그래서 메소드 하나로 양방향 관계를 모두 설정하도록 변경할 수 있다.
public class Member {
...
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
}
- 연관관계를 변경할 때는 기존 팀이 있으면 기존 팀과 회원의 연관관계를 삭제하는 코드를 추가해야 한다.
public class Member {
...
public void setTeam(Team team) {
if (this.team != null) {
this.team.getMembers().remove(this);
}
this.team = team;
team.getMembers().add(this);
}
}
반응형