0. 개요
엔티티들은 대부분 다른 엔티티와 연관관계가 있다.
하지만, 객체는 참조를 사용해서 관계를 맺고, 테이블은 외래 키를 사용해서 관계를 맺는 다른 특징을 가지고 있고, ORM에 있어서 어려운 부분이 객체 연관 관계 <-> 테이블 연관관계의 매핑이다.
1. 단방향 연관관계
: 회원과 팀의 관계
-> (N:1)의 단방향 관계로 가정
위에 보다시피
: 객체 연관관계
-> Member.team 필드로 팀 객체와 연관관계를 맺는다.
-> 회원 객체와 팀 객체는 단방향 관계이다.
: 테이블 연관관계
-> TEAM_ID 외래 키를 통해 팀 테이블과 연관관계를 맺는다.
-> 회원 테이블과 팀 테이블은 양방향 관계이다.
-> 회원 테이블의 TEAM_ID 외래 키를 통해 회원과 팀을 조인할 수 있고, 팀과 회원도 조인할 수 있다.
= 객체 연관관계와 테이블 연관관계의 큰 차이
-> 참조를 통한 연관관계는 언제나 단방향이다.
-> 객체에서의 연관관계를 양방향으로 만들고자 한다면 반대쪽에도 필드를 추가해 참조를 보관해야 한다.
-> 하지만 반대쪽에도 참조를 보관하더라도 양방향은 아니고 서로 다른 단방향 연관관계가 2개인 것이다.
* 정리
-> 객체는 참조로 연관관계를 맺는다.
-> 테이블은 외래 키로 연관관계를 맺는다.
-> 참조를 사용하는 객체의 연관관계는 단방향이다.
-> 외래키를 사용하는 테이블의 연관관계는 양방향이다.
1-1. JPA를 통한 객체 관계 매핑
객체는 참조를 사용해 연관관계 탐색 (= 객체 그래프 탐색)
테이블은 외래 키를 사용해 연관관계 탐색 (= 조인)
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
private String username;
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
public void setTeam(Team team){
this.team = team;
}
//Getter, Setter ...
}
Member Entity
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
//Getter, Setter ...
}
Team Entity
: @ManyToOne
-> 다대일 관계라는 매핑 정보이다.
-> 연관관계 매핑시에는 다중성을 나타내는 어노테이션을 필수로 사용해야 한다.
->
속성 | 기능 | 기본값 |
optional | false로 설정하면 연관된 엔티티가 항상 있어야 한다. | true |
fetch | 글로벌 패치 전략을 설정한다. | @ManyToOne = FetchType.EAGER @OneToMany = FetchType.LAZY |
cascade | 영속성 전이 기능을 사용한다. |
: @JoinColumn(name="TEAM_ID")
-> 외래 키를 매핑할 때 사용한다.
-> name 속성에는 매핑할 외래 키 이름을 지정한다.
-> 이 어노테이션을 생략할 수도 있다.
->
속성 | 기능 | 기본값 |
name | 매핑할 외래 키 이름 | 필드명+ _ + 참조하는 테이블의 기본 키 컬럼명 |
referencedColumnName | 외래 키가 참조하는 대상 테이블의 컬럼명 | 참조하는 테이블의 기본 키컬럼명 |
foreginKey(DDL) | 외래 키 제약조건을 직접 지정할 수 있다. | |
unique, nullable, insertable, updatable, columnDefinition, table |
2. 양방향 연관관계
: 팀과 회원의 관계
-> (1:N) 일대다의 양방향 관계로 가정
일대다 관계는 여러 연관관계를 맺을 수 있기 때문에 컬렉션(List)을 사용해야 한다.
테이블의 관계는 단방향 그래프를 설명할때와 동일하다.
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
private String username;
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
//연관관계 설정
public void setTeam(Team team){
this.team = team;
}
//Getter, Setter...
}
회원 엔티티는 단방향 엔티티와 동일하다
@Entity
public class Team{
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<Member>();
//Getter, Setter...
}
반면 팀 엔티티의 경우 @OneToMany 매핑 정보와 List<Member> members가 추가되었다.
3. 연관관계의 주인
엔티티를 양방향 연관관계로 설정할 경우 객체의 참조는 둘이지만, 외래 키는 하나로 둘 사이에 차이점이 있다.
두 객체 연관관계 중 하나를 정해 테이블의 외래키를 관리해야 하는데 이를 연관관계의 주인이라 한다.
3-1. 양방향 매핑 규칙
양방향 연관관계 매핑 시에 두 연관관계 중 하나를 연관관계의 주인으로 정해야 한다.
연관관계의 주인만 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)할 수 있다.
주인이 아닌 쪽은 읽기만 가능하다.
-> 주인은 mappedBy 속성을 사용하지 않는다.
-> 주인이 아니면 mappedBy 속성을 사용해 속성의 값으로 연관관계의 주인을 지정해야 한다.
위의 회원 및 팀과 관련된 양방향 관계에서 회원 테이블에 있는 TEAM_ID를 연관관계 주인으로 설정해야 한다.
-> 연관관계의 주인은 테이블에 외래 키가 있는 곳으로 설정해야 한다.
-> Team.members에 mappedBy="team" 속성을 사용해야 하는데 이것은 회원 테이블에 team 필드를 말한다.
* 정리
-> 연관관계의 주인만이 데이터베이스 연관관계와 매핑되어 외래 키를 관리할 수 있다.
-> 주인이 아닌 반대편은 읽기만 가능하고 외래키를 변경하지 못한다.
-> 다대일, 일대다 관계에서는 '다' 쪽이 외래 키를 가진다.
4. 양방향 연관관계의 주의점
4-1. 순수한 객체까지 고려
객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전하다.
-> JPA를 사용하지 않는 순수한 객체 상태에서 심각한 문제가 발생할 수 있기 때문
public void test_양방향() {
//팀1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
Member member1 = new Member("member1", "회원1");
//양방향 연관관계 설정
member1.setTeam(team1);
team1.getMembers().add(member1);
em.persist(member1);
}
4-2. 연관관계 편의 메소드
연관관계 주인만 관리하면 되는 줄 알았는데, 순수 객체를 위해서는 결국 양방향 모두 입력해줘야한다.
이런 불편함을 해소하고자 편의 메소드를 사용하면 된다.
public class Member{
private Team team;
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
...
}
set을 할때 함께 넣어주면 된다.
하지만 이 경우에도 문제가 발생할 수 있는데
member1.setTeam(team1);
member1.setTeam(team2);
다음과 같이 작성할 경우, team1에도 member1이, team2에도 member1이 존재하는 문제가 발생한다.
그래서, 편의 메소드를 작성할 경우에는 연관관계를 삭제하는 것에도 주의를 기울일 필요가 있다.
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);
}
...
}
'Back-end > JPA' 카테고리의 다른 글
[JPA] 자바 ORM 표준 JPA 프로그래밍 (3) - 엔티티 매핑 (2) | 2024.12.18 |
---|---|
[JPA] 자바 ORM 표준 JPA 프로그래밍 (2) - 영속성 관리 (0) | 2024.12.17 |
[JPA] 자바 ORM 표준 JPA 프로그래밍 (1) - JPA란? (0) | 2024.12.17 |