테이블 환경에서는 JOIN문을 이용하여 두 테이블간의 양방향 매핑이 가능하지만, 객체 환경에서라면 말이 달라진다.
그렇다면 객체에서는 어떻게 단방향/양방향 매핑을 구현할 수 있는지 알아보자.
참고로, JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 한다는 점에 주의하자.
1. 단방향 매핑
단순히 객체의 멤버로 객체를 지정하면 된다.
<Member.java>
package com.example.jpastudy.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
public class Member {
@Id
@GeneratedValue
private Long id;
@Column(name="USERNAME", unique = true)
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private Team team;
// 결과 확인용
@Override
public String toString() {
return "Member{" +
"id=" + id +
", name='" + name + '\'' +
", team=" + team +
'}';
}
}
- @ManyToOne Annotation은 N:1관계의 객체임을 알린다. Member은 Team에 속해있는 관계인 것이다. fetch 옵션값인 FetchType.LAZY는 지연 로딩 옵션이며, 현업에서는 이를 권장하고 있다고 한다.
- @JoinColumn Annotation은 외래키를 매핑할 때 사용한다. name 속성으로 매핑할 외래키의 이름을 직접 지정할 수도 있으며, 생략시 name의 기본값은 [필드명+_+참조테이블의 기본 키 컬럼명]이다.
<Team.java>
package com.example.jpastudy.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
@Getter
@Setter
public class Team {
@Id
@GeneratedValue
private Long id;
private String name;
// 결과 확인용
@Override
public String toString() {
return "Team{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
2. 양방향 매핑
N:1관계에서 N측 객체안에 1측 객체에 대한 리스트를 추가하면 된다. 즉, 양방향 매핑의 장점은 반대 방향으로도 객체 탐색이 가능한 것이다.
객체 환경에서는 양측에 단방향 매핑을 적용함으로써 양방향 매핑을 구현할 수 있다.
<Member.java>
package com.example.jpastudy.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
public class Member {
@Id
@GeneratedValue
private Long id;
@Column(name="USERNAME", unique = true)
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private Team team;
// 결과 확인용
@Override
public String toString() {
return "Member{" +
"id=" + id +
", name='" + name + '\'' +
", team=" + team.getName() +
'}';
}
}
- toString()에서 team을 참조하게 되면 두 객체가 서로 무한으로 참조하게 되면서 StackOverflow 현상이 발생한다. 그래서 team.getName()으로 대신 객체를 식별할 수 있게 했다. toString() 메소드가 아니더라도 무한 참조를 일으킬 수 있는 메소드에는 조건문을 걸어서라도 무한 참조를 방지하도록 하자.
<Team.java>
package com.example.jpastudy.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Setter
public class Team {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
// 결과 확인용
@Override
public String toString() {
return "Team{" +
"id=" + id +
", name='" + name + '\'' +
", members=" + members +
'}';
}
}
- @OneToMany Annotation은 1:N관계의 객체임을 알린다. mappedBy 속성은 양방향 매핑일 때 사용하며, 연관관계의 주인을 지정하는 속성이다. mappedBy 속성으로 매핑 설정한 요소에 따로 값을 추가하지 않아도 DB에는 외래 키 값이 정상적으로 입력되지만, 두 쪽 다 값을 지정해주는 것이 객체로 관리한다는 취지에 맞다.
※ 연관관계의 주인★ : 두 객체 연관관계 중에서 외래 키가 있는 곳을 말한다. 일대다/다대일 관계에서는 항상 다 쪽이 외래 키를 가진다. 연관관계의 주인은 DB 연관관계와 매핑되고 외래 키를 관리할 수 있다. 반면에 연관관계의 주인이 아닌 쪽에 입력한 값은 DB에 저장될 때 무시된다.
※ mappedBy 속성값은 매핑할 객체 이름을 대소문자까지 일치하게 작성해야 한다.
- 객체의 다대일 양방향 연관관계에서 양쪽 관계를 편리하고 안전하게 맺어주기 위해, 연관관계 주인의 setter 메소드는 다음과 같이 개선해주는 것이 좋다.
public void setTeam(Team team) {
if(this.team!=null) this.team.getMembers().remove(this); // 기존 관계가 있었다면 제거
this.team=team;
team.getMembers().add(this);
}
'Spring 사전 준비 > JPA Hibernate' 카테고리의 다른 글
[JPA] 고급 매핑 기술 (0) | 2022.07.28 |
---|---|
[JPA] 일대일 매핑과 다대다 매핑 (0) | 2022.07.26 |
[JPA] EntityManager / persistence.xml (0) | 2021.08.16 |
[JPA] Entity Annotations (0) | 2021.08.16 |
[JPA] JPA 소개 및 프로젝트 준비 (0) | 2021.08.14 |
댓글