연관관계 매핑 - 단방향 연관관계
★ 연관관계가 필요한 이유
1. 회원과 팀이 존재
2. 회원은 하나의 팀에만 소속될 수 있음
3. 회원과 팀은 다대일 관계
- 해당 시나리오를 테이블에 맞춰서 객체를 모델링 한다고 가정해보겠다
Member 테이블 : MEMBER_ID(PK), TEAM_ID(FK), USERNAME
Team 테이블 : TEAM_ID(PK), NAME
Member 엔티티 : id, teamId, name
Team 엔티티 : id, name
- 엔티티를 작성해준 후, 팀과 멤버를 저장하는 로직을 구성해보겠다
- Member 정보를 저장하고, 팀 아이디를 저장하려면 팀 객체를 생성해서 영속성 컨텍스트를 생성하고 멤버에 team.getId()를 이용해서 팀 아이디를 가져와야하지만, 이는 객체지향적인 설계가 아니다.
- 이번에는 멤버와 해당되는 팀을 조회하는 로직을 구성해보겠다
- 바로 팀 정보를 가지고 올 수 없기 때문에 굉장히 번잡한 과정을 거쳐야 한다.
- 테이블과 객체 사이에는 이러한 큰 간극이 존재하기 때문에, 객체를 테이블에 맞춰 데이터 중심으로 모델링하면 협력 관계를 만들 수 없다
★ 단방향 연관관계
- 객체의 연관관계를 사용해서 객체 지향 모델링을 해보겠다
Member 엔티티 : id, Team team (Team 객체 참조), username
Team 엔티티 : id, name
- Member.team과 MEMBER.TEAM_ID를 매핑하는 것이 연관관계 매핑!!
- 연관관계를 매핑하기 위해서는 새로운 어노테이션들을 사용해야 한다
- @ManyToOne
: 이름 그대로 다대일(N:1) 관계를 담고있는 매핑 정보이다. 회원과 팀은 다대일 관계이며, 이 어노테이션은 ManyToMany, OneToMany 등 방향과 다중성에 따라 다양하게 존재한다. 이 어노테이션은 필수로 사용해야 한다.
속성 | 기능 | 기본값 |
optional | false로 설정하면 연관된 엔티티가 항상 있어야 함 | true |
fetch | 글로벌 페치 전략을 설정 | @ManytoOne = FetchType.EAGER @OneToMany = FetchType.LAZY |
cascade | 영속성 전이 기능 사용 | |
targetEntity | 연관된 엔티티 타입 정보 설정 (이 기능은 거의 사용하지 않음) |
targetEntity 속성 사용 예
@OneToMany
private List<Member> members; //제네릭으로 타입 정보 알 수 있음
@OneToMany(targetEntity=Member.class)
private List members; //제네릭이 없을 때 타입 정보 알 수 있음
- @JoinColumn(name = "")
: 조인 컬럼은 외래 키를 매핑할 때 사용하며, name 속성에는 매핑할 외래 키 이름을 지정한다. 이 어노테이션은 생략할 수 있다.
속성 | 기능 | 기본값 |
name | 매핑할 외래 키 이름 | 필드명 + _ + 참조하는 테이블의 PK 컬럼명 |
referencedColumnName | 외래키가 참조하는 대상 테이블의 컬럼명 | 참조하는 테이블의 PK 컬럼명 |
foreignKey(DDL) | 외래키 제약조건을 직접 지정할 수 있음. 테이블을 생성할 때만 사용 |
|
unique nullable insertable updatable columnDefinition table |
@Column의 속성과 같음 |
- 객체지향 모델링을 통해서 등록, 조회 로직을 구성해보겠다
- 간단하게 setTeam(team)으로 등록을 할 수 있고 멤버의 팀을 조회할 때도 findMember.getTeam()을 통해서 편하게 팀 정보를 가져올 수 있다. 해당 로직을 실행해보면 외래키 제약조건들이 생성된 DDL을 확인할 수 있다.
- 객체지향 모델링을 통해서 수정 로직을 구성해보겠다
- 새로운 팀을 생성하고 member.setTeam(team3)만 해주면 멤버의 팀 정보도 간편하게 수정할 수 있다.