-
[JPA] 프로그래밍 : 1대다 맵핑JPA 2020. 4. 3. 15:51
연관 관계
- 회원과 팀
- 회원은 하나의 팀에만 소속
- 회원가 팀은 다대 일 관계
관계에는 항상 두 엔티티가 존재 한다.
- 둘 중 하나는 관계의 주인(owning)
-
다른 쪽은 종속된 쪽 (non-owning)
-
단반향 관계에서는 관계를 정의한 쪽(레퍼런스를 가지고 있는 쪽)이 주인
-
관계의 주인에 매핑정보를 삽입해야 한다.
-
양방향 관계에서는 @ManyToOne을 설정한 쪽이 주인
-
@onToMaynay(mappedBy)
-
단방향과 마찬가지로 관계의 주인에게 매핑 관계를 설정해야 DB에 반영
단방향 관계 ( N : 1 )
@Entity public class Account{ @Id @GeneratedValue private Long id; private String userName; } @Entity public class Study{ @Id @GeneratedValue private Long id; private Strng name; @ManyToOne // N : 1 관계 private Account owner; } // Account account = new Account(); account.setUserName("user1"); Study study = new Study(); study.setName("Spring Data JPA"); study.setOwner(account); Session session = entityManager.unwrap(Session.class); session.save(account); session.save(study);
- Study라는 Table안에 Account PK를 갖는 외래키가 생성
- Study가 주인(owning) / Account는 종속 된 쪽(non-owning)
- N : 1 단방향 관계
단방향 관계 ( 1 : N )
@Entity public class Account{ @Id @GeneratedValue private Long id; private String name; @OneToMany // 1 : N 관계 private Set<Study> studies = new HashSet<>(); } @Entity public class Study{ @Id @GeneratedValue private Long id; private Strng name; }
- Account가 주인(owning) / Study는 종속 된 쪽(non-owning)
- 1 : N 단방향 관계
- @oneToMany(1 : N 관계) 설정 시, 해당 관계를 관리하는 테이블이 생성
양방향 관계
: 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다. 엔티티들 단방향으로 매핑하면 참조를 하나만 사용하므로 이 참조로 외래 키를 관리하면 된다.
엔티티를 양방형으로 매핑하면 서로를 참조한다. 따라서 객체의 연관관계를 관리하는 포인트는 2곳으로 늘어난다. 하지만 엔티티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데 외래 키는 하나다. 따라서 둘 사이에 차이가 발생한다. 그렇다면 둘 중 어던 관계를 사용해서 외래키를 관리해야 할까?
이런 차이로 인해 JPA에서는 두 객체의 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야 하는데, 이것을 연관관계의 주인(Owner)라 한다.
-> 연관관계의 주인을 정한다는 것은 사실 외래 키 관리자를 선택하는 것
: 연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래키를 관리(등록, 수정, 삭제)할 수 있다. 반면에 주인이 아닌 쪽은 읽기만 할 수 있다.
@Entity public class Account{ @Id @GeneratedValue private Long id; private String name; @OneToMany(mappedBy = "owner") // 1 : N 관계 private Set<Study> studies = new HashSet<>(); } @Entity public class Study{ @Id @GeneratedValue private Long id; private Strng name; @ManyToOne // N : 1 관계 private Account owner; }
- 1대다 관계에서 @ManyToOne ( N : 1 관계)로 설정 한 쪽이 주인
- @OneToMany에 mappedBy (주인) 을 설정을 꼭 해줘야 한다. (설정하지 않으면 단방향 관계로 설정)
- 관계의 주인에게 매핑을 설정해야 DB에 반영
Account account = new Account(); account.setUserName("user1"); Study study = new Study(); study.setName("Spring Data JPA"); //관계의 주인은 study이므로 study에게 account를 매핑 study.setOwner(account); Session session = entityManager.unwrap(Session.class); session.save(account); session.save(study);
양방향 관계 주의점
1. 양방형 연관관계를 설정하고 가장 흔히 하는 실수는 연관관계의 주인에는 값을 입력하지 않고, 주인이 아닌 곳에만 값을 입력하는 것이다. 데이터베이스에 외래 키 값이 정상적으로 저장되지 않으면 이것부터 의심해보자.
public void testSaveNonOwner() { Member member1 = new Member("member1", "회원1"); em.persist(member1); Member member2 = new Memeber("member2","회원2"); em.persist(member2); Team team1 = new Team("team1", "팀"); team1.getMember().add(member1); team2.getMember().add(member2); em.persist(team1); } /// 데이터베이스 member 조회 SELECT * FROM MEMBER; : 결과 member1 -> TEAM_ID = null member2 -> TEAM_ID = null;
-> 이 예제 코드는 연관관계의 주인인 Member.team에 아무 값도 입력하지 않았다. 따라서 TEAM_ID 외래키 값도 Null
2. 순수한 객체까지 고려한 양방향 연관관계
: 객체 관점에서는 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전하다. 양쪽 방향 모두 값을 입력하지 않으면 JPA를 사용하지 않은 순수한 객체 상태에서 심각한 문제가 발생 할 수 있다.
public void test순수한객체_양방향() { Team team1 = new Team("team1","팀1"); Member member1 = new Member("member1", "회원1"); Member member2 = new Member("member2", "회원2"); member1.setTeam(team1); member2.setTeam(team1); List<Member> members = team1.getMembers(); System.out.println(member.size()); // 결과 : 0 }
: JPA를 사용하지 않은 순수한 자바로만 이루어진 객체 -> 우리가 기대하는 양방향 연관관계의 결과가 아님
회원 <-> 팀 양방향 관계를 설정하기 위해,
회원 -> 팀
팀 -> 회원
모두 관계설정을 해줘야 한다.
public void test순수한객체_양방향() { Team team1 = new Team("team1","팀1"); Member member1 = new Member("member1", "회원1"); Member member2 = new Member("member2", "회원2"); member1.setTeam(team1); team1.getMembers().add(member1); member2.setTeam(team1); team1.getMembers().add(member2); List<Member> members = team1.getMembers(); System.out.println(members.size()); // 결과 : 2 }
양방향 연관관계는 결국 양쪽 다 신경써야 한다.
public class Member { private Team team; public void setTeam(Team team) { if(this.team != null) this.team.getMembers().remove(this); this.team = team; team.getMembers().add(this); } } ... member1.setTeam(team1); member2.setTeam(team2);
정리
- 단뱡향 매핑만으로 테이블과 객체의 연관관계 매핑은 이미 완료되었다.
- 단뱡향을 양방향으로 만들면 반대방향으로 객체 그래프 탐색 기능이 추가된다.
- 양방향 연관관계를 매핑하려면 객체에서 양쪽 방향을 모두 관리해야 한다.
참고 : 백기선 스프링 데이터 JPA, JPA 프로그래밍
'JPA' 카테고리의 다른 글
[JPA] 엔티니 매니저 (0) 2020.06.28 [JPA] 엔티티 매핑 (0) 2020.04.06 [JPA] 프로그래밍 : 엔티티 상태 (0) 2020.04.03 [JPA] 프로그래밍 : 엔티티 (0) 2020.04.02 [JPA] 개념 (0) 2020.04.02 댓글