1. 연관관계가 필요한 이유
조영호(『객체지향의 사실과 오해』)는 객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이라고 말한다.
즉, 객체 간의 관계를 명확히 설정하여 객체들이 서로 원활하게 협력할 수 있도록 하는 것이 중요하다.
객체와 테이블의 관계 차이
비교 항목 | 객체 | 테이블 |
연관관계 표현 | 참조 필드(team) 사용 | 외래 키(TEAM_ID) 사용 |
탐색 방식 | 객체의 참조(member.getTeam()) | 조인(SQL JOIN) 사용 |
다중성 | 일대일(1:1), 다대일(N:1), 일대다(1:N), 다대다(N:M) | 같은 개념이지만 SQL로 직접 구현 |
예제 시나리오
- Member(회원)과 Team(팀)이 존재
- 회원은 하나의 팀에만 소속될 수 있음
- 회원과 팀은 다대일(N:1) 관계
2. 단방향 연관관계
단방향 연관관계에서는 한쪽에서만 참조할 수 있다.
즉, Member는 Team을 참조하지만, Team은 Member를 알지 못한다.
❌ 연관관계가 없는 객체 모델링 (잘못된 방법)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@Column(name = "TEAM_ID")
private Long teamId; // FK를 직접 사용 (비객체지향적)
}
문제점
- teamId를 직접 다루기 때문에 객체 관계가 아닌 데이터 중심적인 설계가 됨
- find(Member.class, memberId)로 Member를 조회한 후,
다시 find(Team.class, teamId)를 해야만 Team을 가져올 수 있음 - 객체 그래프 탐색이 불가능 (member.getTeam() 불가)
단방향 연관관계 - 객체 지향적인 설계
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne
@JoinColumn(name = "TEAM_ID") // 외래 키 매핑
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
}
장점
- Member에서 Team을 직접 참조 (member.getTeam())
- SQL을 몰라도 객체 그래프 탐색 가능
조회 예제
Member member = em.find(Member.class, memberId);
Team team = member.getTeam(); // 객체 참조로 접근 가능
3. 양방향 연관관계와 연관관계의 주인
양방향 연관관계에서는 양쪽에서 참조가 가능하다.
즉, Member는 Team을 참조하고, Team도 Member 목록을 가질 수 있다.
양방향 연관관계 매핑
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne
@JoinColumn(name = "TEAM_ID") // 연관관계의 주인
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team") // team 필드를 참조하는 주인이 있음
private List<Member> members = new ArrayList<>();
}
연관관계의 주인 (mappedBy)가 Member가 되어야 하는 이유
1. 외래 키(FK)가 Member 테이블에 존재하기 때문
- 데이터베이스에서 TEAM_ID 컬럼이 Member 테이블에 있으므로,
Member 엔티티가 외래 키를 관리하는 것이 논리적으로 맞음.
2. 객체의 일관성을 유지할 수 있음
- 연관관계 주인이 아니면 값을 저장할 수 없으므로,
team.getMembers().add(member); 같은 코드만 실행해도
실제 데이터베이스에는 반영되지 않음. - member.setTeam(team);으로 명확하게 설정해야만 저장됨.
조회 예제 (반대 방향 조회)
Team team = em.find(Team.class, teamId);
int memberSize = team.getMembers().size(); // 역방향 탐색 가능
연관관계 편의 메소드
public void setTeam(Team team) {
this.team = team;
if (!team.getMembers().contains(this)) {
team.getMembers().add(this);
}
}
4. 정리
- 객체와 테이블은 연관관계를 맺는 방식이 다름
- 단방향 연관관계: 객체에서 참조를 활용하여 외래 키를 매핑 (@ManyToOne)
- 양방향 연관관계: 객체 그래프 탐색이 가능 (@OneToMany(mappedBy))
- 연관관계의 주인은 외래 키가 있는 엔티티가 되어야 함 (Member)
- 연관관계 편의 메소드를 사용하여 관계를 명확하게 관리
반응형
'Backend > JPA' 카테고리의 다른 글
JPA 고급 매핑 - 상속관계 매핑과 @MappedSuperclass (테이블 구조 상속화 및 공통 엔티티 관리) (0) | 2025.02.21 |
---|---|
JPA 다양한 연관관계 매핑 개념(다중화, 일대일, 다대다, 다대일, 일대다) (0) | 2025.02.21 |
JPA 기본 매핑 - 객체, 테이블, 컬럼 및 기본 키 매핑 (1) | 2025.02.19 |
JPA - 영속 상태와 준영속 상태의 차이 및 준영속 상태 만드는 방법 (0) | 2025.02.12 |
JPA - flush()란? (0) | 2025.02.12 |