다대일(N:1) 관계 매핑 정보다.
속성명 | 의미 |
---|---|
optional |
false로 설정 시 연관된 엔티티가 항상 있어야한다 (기본값 : true) |
fetch |
글로벌 Fetch 전략을 설정한다 |
cascade |
영속성 전이 기능을 사용한다 |
targetEntity |
연관된 엔티티의 타입 정보를 설정한다. (거의 사용안함) |
외래 키를 매핑할 때 사용한다.
속성명 | 의미 |
---|---|
name |
매핑할 외래키 이름 (기본값 : 필드명 + _ + 참조하는 테이블의 기본 키 컬럼명) |
referencedColumnName |
외래 키가 참조하는 대상 테이블의 컬럼명 (기본값 : 참조하는 테이블의 기본 키 컬럼명) |
foreignKey |
테이블을 생성할 때만 사용하는 속성으로 외래 키 제약조건을 직접 지정할 수 있다 |
단방향 관계를 매핑을 할 때 다대일(@ManyToOne) 또는 일대일(@OneToOne) 중 어떤 것을 사용할 지는 반대편 관계에 달려있다.
반대 편이 일대다(@OneToMany)면 다대일(@ManyToOne), 반대편이 일대일(@OneToOne)이면 일대일(@OneToOne)
사실 테이블에서는 하나의 외래키를 통해 두 테이블의 연관관계를 관리한다.
하지만 객체는 단방향 관계 2개를 사용하여 두 객체간 연관관계를 관리한다.
JPA서는 연관관계의 주인을 정함으로써 이 문제를 해결한다.
테이블의 외래키를 관리하는 엔티티를 연관관계의 주인이라 하고 외래키를 보유한 테이블이 연관관계의 주인이 되어야한다.
주인이 아닌 엔티티는 mappedBy 속성을 이용해 주인을 지정해야한다.
연관관계의 주인만이 외래 키에 영향을 줄 수 있다. 때문에 주인이 아닌 엔티티를 통해 add등을 수행해도 INSERT 되지 않는다.
오직 연관관계의 주인만이 연관관계 설정에 영향을 줄 수 있다.
하지만 순수 객체의 상태에서는 이야기가 다르다.
주인이 아닌 측에서 외래키에 영향을 줄 수 없다고 관계를 맺어두지 않으면 DB에는 잘 들어갈 지라도 객체상태에서는 사용이 불가능한 경우가 있다.
때문에 양방향 관계가 설정된 엔티티들을 사용할땐 양 쪽 둘다 연관관계를 맺어주자.
Team (팀) / Member (회원)
Team team = new Team();
team.setTeamName("T1");
entityManager.persist(team);
Member m = new Member();
m.setMemberName("M1");
m.setTeam(team); // 회원 -> 팀 연관관계 설정
entityManager.persist(m);
위 코드에서는 회원 -> 팀으로의 방향으로만 연관관계를 설정했다.
하지만
Team team = new Team();
team.setTeamName("T1");
entityManager.persist(team);
Member m = new Member();
m.setMemberName("M1");
m.setTeam(team); // 회원 -> 팀 연관관계 설정
team.getMembers().add(m); // 팀 -> 회원 연관관계 설정 (실제 JPA 수행로직엔 영향 주지않음)
entityManager.persist(m);
위와 같이 양방향 연관관계를 모두 맺어주어 순수 객체 상태에서도 안전하게 사용가능한 상태로 만들어 주는것이 좋다.
그리고 보통 위 처럼 양 쪽 다 신경쓰는 경우 실수로 지정하지 않는 경우가 발생할 수 있다.
때문에 연관관계 주인 측에서 완벽하게 맺어주도록 지정하는 것이 좋다.
Member class
public void setTeam(Team team) {
this.team = team; // 회원 -> 팀
team.getMembers().add(this); // 팀 -> 회원
}
위 처럼 연관관계 편의 메소드를 작성하여 실수를 방지할 수 있다.
하지만 위 코드에도 치명적인 문제가 있다.
team의 members는 연관관계의 주인이 아니기 때문에 새 팀이 설정될 때 영속성 컨텍스트가 flush되지 않으면 team의 members 리스트 안에 자신이 남아 있는 문제가 발생한다.
public void setTeam(Team team) {
// 현재 소속팀이 있으면 해당 팀의 회원목록에서 자신을 제거
if (this.team != null) {
this.team.getMembers().remove(this);
}
this.team = team; // 회원 -> 팀
team.getMembers().add(this); // 팀 -> 회원
}
위 처럼 로직을 추가하여 보다 견고하게 양방향 연관관계를 관리하도록 할 수 있다.
자바 ORM 표준 JPA 프로그래밍을 공부하며 정리한 내용입니다.