Spring/JPA
연관관계 매핑
by o3oppp
2023. 12. 19.
객체 연관관계 (2개)
- 회원 -> 팀 연관관계 1개(단방향)
- 팀 -> 회원 연관관계 1개(단방향)
- 객체의 양방향 관계는 서로 다른 단방향 관계 2개
- 객체는 참조를 사용해서 연관된 객체를 찾음
- 객체를 양방향으로 참조하려면 단방향 연관관계 2개를 만들어야 함
테이블 연관관계 (1개)
- 회원 <-> 팀 연관관계 1개(양방향)
- 테이블 양방향 연관관계는 외래키 하나로 두 테이블의 연관관계를 관리
- 테이블은 외래키로 조인을 사용해서 연관된 테이블을 찾음
단방향
- @ManyToOne : 해당 클래스가 N, 연관된 클래스가 1
양방향
- @OneToMany(mappedBy = "연결된 엔티티의 연결된 변수명")
- mappedBy : 나는 매핑이 되었다는 뜻으로 주인인이 아님을 명시
연관관계 주인
- 객체 양방향 연관관계는 관리 주인이 필요
- 객체의 두 관계 중 하나를 연관관계 주인으로 지정
- 연관관계 주인만이 외래키를 관리(등록, 수정). 즉, 외래키가 있는 곳이 주인
- 주인은 mappedBy 속성을 사용하지 않음
- 주인이 아닌쪽은 읽기만 가능
- 주인이 아니면 mappedBy 속성 사용
- 주인이 아닌 방향만 연관관계를 설정하면 안됨
- 양방향 매핑 시 무한 루프 주의
- 주인에는 값을 입력해야 하나 그냥 항상 양쪽에 값을 설정
Team team = new Team();
team.setName("A");
em.persist(team);
Member member = new Member();
member.setName("user1");
//주인이 아닌 방향에만 연관관계 설정 : MEMBER TABLE 조회 시 TEAM_ID는 null
team.getMembers().add(member);
//연관관계 주인 또한 값 설정
member.setTeam(team);
em.persist(member);
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
// 참조 대신 외래키를 그대로 사용
@Column(name = "TEAM_ID")
private Long teamId;
...
}
...
// 팀 저장
Team team = new Team();
team.setName("A");
em.persist(tema);
// 회원 저장
Member member = new Member();
member.setName("user1");
// 외래키 식별자를 직접 다룸
member.SetTeamId(team.getId());
// 조회
Member findMember = em.find(Member.class, member.getId());
// 연관관계가 없음
Team findTeam = em.find(Team.class, team.getId());
고려사항
다중성
- 다대다
- 일대다
- 일대일
- @OneToOne
- 주 테이블이나 대상 테이블 중 외래키 선택 가능
- 주 테이블에 외래키
- 장점 : 주 테이블 조회 시 대상 테이블에 데이터 유무 확인 가능
- 단점 : 값이 없으면 외래키에 null 허용
- 대상 테이블에 외래키
- 장점 : 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지
- 단점 : 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩 됨
- 다대다
- @ManyToMany
- 대체로 실무에서 사용하지 않음
단방향, 양방향
- 테이블
- 객체
- 참조용 필드가 있는쪽으로만 참조 가능
- 한쪽만 참조하면 단방향, 양쪽이 서로 참조하면 양방향
연관관계 주인
- 테이블은 외래키 하나로 두 테이블이 연관관계를 맺음
- 객체 양방향 관계는 A->B, B->A 처럼 참조가 2곳, 둘 중 테이블의 외래키를 관리할 곳을 지정
- 외래키를 관리하는 클래스, 참조가 연관관계 주인
- 주인의 반대편 : 단순 조회만 가능(읽기만 가능)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
// 참조 대신 외래키를 그대로 사용
/*
@Column(name = "TEAM_ID")
private Long teamId;
*/
// 객체의 참조와 테이블의 외래키를 매핑
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
...
// 팀 저장
Team team = new Team();
team.setName("A");
em.persist(tema);
// 회원 저장
Member member = new Member();
member.setName("user1");
// 외래키 식별자를 직접 다룸
/*member.SetTeamId(team.getId());*/
// 연관관계 저장
member.setTeam(team); // 단방향 연관관계 설정, 참조 저장
em.persist(member);
// 조회
Member findMember = em.find(Member.class, member.getId());
// 연관관계가 없음
/*Team findTeam = em.find(Team.class, team.getId());*/
// 참조로 연관관계 조회 - 객체 그래프 탐색
Team findTeam = findMember.getTeam();