목차
양방향 연관관계의 문제점
객체에서는 각기 다른 객체에서 참조하는 객체를 업데이트 하는 방법이 두 가지가 발생한다.
양방향 연관관계의 해결 방법
객체의 두 관계중 하나를 연관관계의 주인 으로 지정해 주인만이 외래 키를 관리하게 한다.(등록, 수정)
주인이 아닌 쪽은 읽기만 가능하다
양방향 연관관계 주인 정하기
Foreign Key (외래키) 가 있는 곳을 주인으로 정해라
- 연관관계의 주인은 mappedBy 속성을 사용할 수 없다.
- 연관관계 주인이 아니면 mappedBy 속성을 사용할 수 있다.
연관관계 주인이 아닌 객체에 데이터 업데이트
Member member = new Member(); member.setUsername("member1"); entityManager.persist(member);
Team team = new Team(); team.setName("TeamA"); team.getMembers().add(member); entityManager.persist(team);
|
MEMBER 테이블 내 TEAM_ID 값이 들어가지 않은 것을 확인할 수 있다.
연관관계 주인인 객체에 데이터 업데이트
Team team = new Team(); team.setName("TeamA");
entityManager.persist(team);
Member member = new Member(); member.setUsername("member1"); member.setTeam(team); entityManager.persist(member);
|
MEMBER 테이블 내 TEAM_ID 값이 정상적으로 들어간 것을 확인할 수 있다.
양방향 연관 관계 주의
양쪽에 데이터를 Setting 해 준다.
데이터가 영속성 Context 내에만 존재할 경우 연관된 데이터 조회시 정상적으로 조회하지 못하는 문제가 발생한다.
순수 객체 상태 를 고려해 항상 양쪽에 값을 설정한다.
- Team과 Member는 영속성 Context에만 데이터가 있는 상태
- Team 내 Member List에는 현재 아무 값이 없는 상태
- 결과적으로 데이터를 못가져오게 된다.
Team team = new Team(); team.setName("TeamA"); em.persist(team);
Member member = new Member(); member.setUsername("member1"); member.setTeam(team); em.persist(member);
Team findTeam = em.find(Team.class, team.getId()); List<Member> members = findTeam.getMembers(); System.out.println("=========================="); for (Member m : members) { System.out.println("m = " + m.getUsername()); } System.out.println("==========================");
|
Team team = new Team(); team.setName("TeamA"); em.persist(team);
Member member = new Member(); member.setUsername("member1"); member.setTeam(team); em.persist(member);
team.getMembers().add(member);
Team findTeam = em.find(Team.class, team.getId()); List<Member> members = findTeam.getMembers(); System.out.println("====================================================================="); for (Member m : members) { System.out.println("m = " + m.getUsername()); } System.out.println("=====================================================================");
|
Flush와 Clear를
- team.getMembers().add(member); 값을 넣지 않아도 데이터가 정상적으로 출력 된다.
Team team = new Team(); team.setName("TeamA"); em.persist(team);
Member member = new Member(); member.setUsername("member1"); member.setTeam(team); em.persist(member);
em.flush(); em.clear();
Team findTeam = em.find(Team.class, team.getId()); List<Member> members = findTeam.getMembers(); for (Member m : members) { System.out.println("m = " + m.getUsername()); }
|
연관관계 편의 메서드 생성
Member 에 생성
public void changeTeam(Team team){ this.team = team; team.getMembers().add(this); }
|
@JoinColumn
는 name 속성에 Mapping 할 외래 키를 지정한다.
@Entity public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "MEMBER_ID") private long id;
@Column(name = "USERNAME") private String username;
@ManyToOne @JoinColumn(name = "TEAM_ID") private Team team;
public void changeTeam(Team team){ this.team = team; team.getMembers().add(this); } }
|
Team 에 생성
public void addMember(Member member){ member.setTeam(this); members.add(member); }
|
mappedBy
는 양뱡향 연관관계일때 반대쪽에 매핑되는 Entity 필드 값을 준다.
@Entity public class Team { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "TEAM_ID") private Long id; private String name;
@OneToMany(mappedBy = "team") private List<Member> members = new ArrayList<>();
public void addMember(Member member){ member.setTeam(this); members.add(member); } }
|
양방향 매핑시 무한 루프를 조심하자
Member에서 toString 생성시 Team 객체를 만날 경우 Team 객체의 toString을 불러온다. Team 객체내 toString은 Member 객체의 toString을 불러오면서 무한 루프가 생기게된다.
Member.java
@Override public String toString() { return "Member{" + "id=" + id + ", username='" + username + '\'' + ", team=" + team + '}'; }
|
Team.java
@Override public String toString() { return "Team{" + "id=" + id + ", name='" + name + '\'' + ", members=" + members + '}'; }
|