단방향
저장
JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 함
Team team1 = new Team("team1", "팀1"); em.persist(team1); Member member1 = new Member("member1", "회원1"); member1.setTeam(team1);// 연관관계 설정 member1 -> team1 em.persist(member1); Member member2 = new Member("member2", "회원2"); member2.setTeam(team1); // 연관관계 설정 member2 -> team1 em.persist(member2);
조회
- 연관관계가 있는 엔티티를 조회하는 방법
- 객체 그래프 탐색
- 객체지향 쿼리 사용(JPQL)
수정
- dirty Checking을 통해 트랜잭션 커밋되면서 자동 반영
연관관계 제거
Member member1 = em.find(Member.class, "member1"); member1.setTeam(null); // 연관관계 제거 /* UPDATE MEMBER SET TEAM_ID=null, .. WHERE ID = 'member1'; */
연관된 엔티티 삭제
- 연관된 엔티티를 삭제하려면 기존에 있던 연관관계를 먼저 제거하고 삭제해야 함
- 그렇지 않으면 외래 키 제약 조건으로 인해 데이터베이스에서 오류가 발생함
member1.setTeam(null); // 회원 1 연관관계 제거 member2.setTeam(null); // 회원 2 연관관계 제거 em.remove(team);
양방향
@OneToMany 단방향 문제점1:N N: 1 양방향 매핑
양방향 매핑 시에는 연관관계의 주인을 설정해주어야 하고, mappedBy를 쓰는 엔티티가 연관관계의 주인이 아닌 쪽임. 연관관계의 주인은 외래키를 갖고 있는 테이블
public class Team{ @OneToMany(cascade=ALL, mappedBy = "team") // mappedBy는 필드이름 private List<Member> members = new ArrayList<>(); } public class Member{ @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name="team_id") // db에서 관리할 컬럼이름 private Team team; }
연관관계 저장
Team team1 = new Team("team1", "팀1"); em.persist(team1); Member member1 = new Member("member1", "회원1"); member1.setTeam(team1); // 연관관계 설정 member1 -> team1 em.persist(member1); Member member2 = new Member("member2", "회원2"); member2.setTeam(team1); // 연관관계 설정 member2 -> team1 em.persist(member2);
- 연관관계의 주인인 Member.team 필드를 통해 회원과 팀의 연관관계를 설정하고 저장함
- 양방향 연관관계는 연관관계의 주인이 외래 키를 관리하여, 주인이 아닌 방향은 값을 설정하지 않아도 데이터베이스에 외래 키 값이 정상 입력됨
team1.getMembers().add(member1); // 무시(연관관계의 주인이 아님) team1.getMembers().add(member2);
양방향 연관관계의 주의점
- 연관관계의 주인에는 값을 입력하지 않고 주인이 아닌 곳에만 값을 입력하면 외래키 값이 정상적으로 저장되지 않음
Member member1 = new Member("member1", "회원1"); em.persist(member1); Team team1 = new Team("team1", "팀1"); team1.getMembers().add(member1); em.persist(team1);
- 이후 member를 조회하면 TEAM_ID값은 null로 조회됨
순수한 객체까지 고려한 양방향 연관관계 → 연관관계 편의 메소드 사용
- 위의 방식으로 member.setTeam() 후 persist를 하면 외래키는 잘 들어가지만 객체에서는 양방향 관계가 맺어지지 않으므로 객체까지 고려한다면 양방향으로 관계를 맺어주어야 함
- 객체 관점에서는 양쪽 방향 모두 값을 입력해주는 것이 가장 안전함
Team team1 = new Team("team1", "팀1"); Member member1 = new Member("member1", "회원1"); Member member2 = new Member("member2", "회원2"); member1.setTeam(team1); // 연관관계 설정 member1 -> team1 member2.setTeam(team1); // 연관관계 설정 member2 -> team1 List<Member> members = team1.getMembers(); System.out.println("members.size = " + members.size()); // 0
Team team1 = new Team("team1", "팀1"); Member member1 = new Member("member1", "회원1"); Member member2 = new Member("member2", "회원2"); member1.setTeam(team1); // 연관관계 설정 member1 -> team1 team1.getMembers().add(member1); // 연관관계 설정 team1 -> member1 member2.setTeam(team1); // 연관관계 설정 member2 -> team1 team1.getMembers().add(member2); // 연관관계 설정 team1 -> member2 List<Member> members = team1.getMembers(); System.out.println("members.size = " + members.size()); // 2
연관관계 편의 메서드
public void setTeam(Team team){ if(this.team != null){ this.team.getMembers().remove(this); } this.team = team; this.getMembers().add(this); } public void addMember(Member member){ this.members.add(member); if(member.getTeam() != this){ // 무한루프에 빠지지 않도록 체크 member.setTeam(this); } }
- 편의 메소드는 한 곳에만 작성하거나 양쪽 다 작성할 수 있는데, 양쪽 다 작성하면 무한루프에 빠지므로 주의하기