지향 설계의 목표는 자율적인 객체들의 협력공동체를 만드는 것이다.
-조영호-
회원, 팀이 있다.
회원은 1개의 팀에만 가입할 수 있고, 팀은 여러 회원을 받을 수 있다.
즉 회원과 팀은 다대일 관계이다. (하나의 팀에 여러개의 회원)
이를 객체로 만들면
package hellojpa;
import javax.persistence.*;
import java.util.Date;
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@Column(name = "TEAM_ID")
private long teamId;
}
package hellojpa;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private long id;
private String name;
}
이렇게 만들어지고 테이블도 생성되게된다. 하지만 이는 연관관계라는것이 없기에 객체지향스럽지 못한 방법이 된다.
테이블은 외래키 조인으로 연관테이블을 찾지만 객체는 참조를 사용해 연관객체를 찾기 때문이다.
Member는 Team에 다대일 관계이다.
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
따라서 ManyToOne 매핑을 해주고, JoinColumn을 통해서 Team의 어떤 Column과 조인 할 지 결정해야한다.
try {
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setUsername("UserA");
member.setTeam(team);
em.persist(member);
Member member1 = em.find(Member.class, member.getId());
Team findTeam = member1.getTeam();
System.out.println("findTeam = " + findTeam.getName());
tx.commit();
}
그 후, 실행해보면
이렇게 Team객체를 바로 꺼낼 수 있다.
하지만 이는 단방향 설정으로 양방향 설정은 또 다른 문제이다.
출처 : 김영한 스프링 JPA 강의
위의 예시에서 양방향 연관관계를 객체에서 구현하려면, Team 소속 Member를 담아서 보여줄 List가 필요하다.
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
일대다관계를 가지고 있으니깐, OneToMany를 설정하고, Member의 Team team 객체와 매핑되어있는것을 표기해줘야한다.
Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();
for (Member m : members) {
System.out.println("m.getUsername = " + m.getUsername());
}
하지만 양방향이 꼭 좋은건 아니다.
연관관계의 주인을 설정하는 기능이다.
객체 연관관계는
회원이 팀으로 맺는 1개의 단방향 연관관계
팀이 회원으로 맺는 1개의 단반향 연관관계
총 2개이다.
반대로 테이블의 연관관계는
FK 하나로 두 연관관계를 관리하는 하나의 관계이다.
따라서 누구의 키로 관리를 할지 주인을 결정하는과정이 바로 mapped 이다.
주인만이 등록 및 수정이 가능하고, 주인이 아닌쪽은 읽기만 가능하다.
주인에는 mappedBy가 붙지 않는다.
보통은 객체를 가지고 있는 경우를 주인으로 설정한다.
위의 예시들의 경우, Member에 Team이 생성되어 있기에 이쪽을 주인으로 하는것이 맞는것이다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
team.getMembers().add(member);
em.persist(member);
잘 보면, Member 테이블에 team 값을 넣지 않았다는것을 알 수 있다.
따라서 주인에는 항상 값을 넣어줘야한다. 하지만, 객체지향적 설계를 고려하면 사실 양쪽에 모두 값을 넣는것이 맞다.
Member 객체의 setTeam을 설정할 때, 연관관계들에 전부 값을 넣어주는 코드를 넣어주는것이 좋다. 또한 Setter 말고 다른 이름이 있는 함수를 쓰는게 유지보수 및 가독성측면에서 유리하다.
또한, 컨트롤러에서는 Entity를 절대로 반환하지 말자. 가능하면 DTO로 변환을 해서 반환하는것을 추천한다.
가능하면 최초에는 단방향 매핑으로 하는것이 좋다. 후에 JPQL같이 역방향 탐색이 필요해지면, 그때 변경하는것이 좋다.