Team에 소속된 Member라는 객체가 있다고 가정하자.
Member는 하나의 팀만 가지고 있고 Team은 여러 Member가 있다고 가정하자
DB테이블을 중심으로 설계한다면 협력관계 (Meber를 통해 Team의 정보를 가져오거나 Team을 통해 Member를 가져오는 것)를 만들 수 없다. DB테이블은 왜래 키를 통한 조인으로 연관된 테이블을 붙혀 가져올수 있지만 객체는 참조를 통해 다른 객체를 찾아오기 때문이다. 이러한 이유로 객체에서는 Team객체를 직점 넣어 연관관계를 부여한다.
객체 지향 언어의 모델링
단방향 매핑
- Member
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
// @Column(name = "TEAM_ID")
// private Long teamId;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
- Team
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
컬럼값으로 teamId를 넣는 것이 아닌 객체를 직접 넣고 조인키로 사용될 컬럼의 정보를 @JoinColumn으로 넣어준다.
@ManyToOne의 경우 Member:Team 의 관계가 多:1 관계임을 의미한다.
양방향 매핑
- Member객체는 단방향 매핑과 동일하다.
- Team 객체
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList();
단방향일 때 Member를 통해 Team의 정보를 가져올 수 있었던 반면 양방향 매핑의 경우 Team 객체를 통해 Team에 포함되어 있는 Member 객체를 List로 가져올 수 있다.
연관관계의 주인과 mappedBy
DB테이블의 경우 양방향 단방향의 개념이 없다. 그 이유는 외래키로 조인 시 아래 쿼리문과 같이 Team과 Member테이블에서 모두 테이블을 불러올 수 있기 때문이다.
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID
하지만 자바의 경우 다르다. 자바는 참조를 통한 연관성 부여임으로 사실상 참조를 통해 단방향 관계를 2번 맺는 시스템 이다.
따라서 두 객체에 모두 존재하는 외래키를 관리할 때 이 두 단방향 연관관계의 외래키중 하나의 외래키가 연관관계의 주인이 된다.
다른 한쪽은 주인이 아닌 읽어오는 값으로 외래키가 존재한다. (중복 변경 누락 등을 방지) 이를 mappedBy 가 수행하고 mappedBy는 연관관계가 맺어진 반대쪽 객체의 인스턴스를 지정하여 해당 인스턴스가 연관관계 주인임을 명시하는 것이다. (위 케이스에서는 Member객체의 team)
따라서 Member객체의 Team은 생성 수정등이 가능한 인스턴스(연관관계 주인)이고 Team 객체에 존재하는List<Member>members의 경우 Member객체의 인스턴스 team의 참조값만 읽어와 해당 Team을 인스턴스로 가지고 있는 Member를 List에 넣어둔 형태이다.
순수한 객체 관계를 고려한다면 양쪽에 모두 값을 넣어주어야 한고 한다. 양쪽에 모두 값을 설정하기 위해 set 메서드와 같은 편의 메서드에 반대쪽 값도 설정해주는 메서드를 사용하면 된다.
필자는 아직 이렇게 까지 사용해본 적은 없다..
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
team.getMembers().add(member);
member.setTeam(team);
em.persist(member);
마지막으로 주의할 점은 양방향 매핑 시 무한 루프를 조심하여야한다.
JSON라이브러리 생성 시 무한 참조가 있을 수 있다. 이런 경우 Entity를 직접 Controller에 주입하여 사용하는 것이 아닌 DAO를 통하여 객체를 전달하는 목적의 객체를 생성하여 전달한다면 무한루프를 피할 수 있다고 한다. JSON 프로잭트를 하면서 적용해 봐야할 것 같다.
'JPA' 카테고리의 다른 글
@MappedSuperclass (0) | 2022.12.20 |
---|---|
상속관계 매핑 (0) | 2022.12.20 |
연관관계 매핑 (0) | 2022.12.20 |
영속성과 엔티티 매핑 (1) | 2022.12.16 |
JAP 기본 원리 및 사용하는 이유 (1) | 2022.12.15 |