객체를 테이블에 맞추어 모델링
예제 시나리오는 다음과 같다.
- 회원과 팀이 있다.
- 회원은 하나의 팀에만 소속될 수 있다.
- 회원과 팀은 다대일(N:1) 관계다.
//회원 객체
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@Column(name = "TEAM_ID")
private Long teamId;
...
}
//팀 객체
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
...
}
회원과 팀 객체를 만들고 회원과 팀을 저장하는 샘플 코드를 작성한다.
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeamId(team.getId());
em.persist(member);
//조회
Member findMember = em.find(Member.class, member.getId());
//연관관계가 없음
Team findTeam = em.find(Team.class, team.getId());
객체는 참조를 사용해 연관된 객체를 찾고, 테이블은 외래키(Foreign Key)로 조인을 사용해 연관된 테이블을 찾는다. 이런 객체와 테이블의 차이가 있기 때문에, 객체를 테이블에 맞추어 테이블 중심으로 모델링하면 협력 관계를 만들 수 없다.
단방향 연관관계
객체의 참조와 테이블의 외래키를 매핑한다.
//회원 객체
@Entity
public class Member {
...
//@Column(name = "TEAM_ID")
//private Long teamId;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
회원 객체의 team
속성을 팀 객체로 변경하고, @ManyToOne
과 @JoinColumn
어노테이션을 붙여준다. @ManyToOne
은 회원과 팀이 N:1 관계이기 때문에 회원 객체 기준 '다'이기 때문에 사용한다.
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
양방향 연관관계
앞에서 회원 객체 -> 팀 객체로 N:1 단방향 연관관계를 설정하였다.
@Entity
public class Team {
...
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
...
}
팀 객체 -> 회원 객체 1:N 단방향 연관관계를 추가로 설정한다. 이렇게 양방향 연관관계가 된다.
연관관계의 주인과 mappedBy
객체와 테이블이 관계를 맺는 방법에는 차이가 있다. 앞의 예시에서 객체 기준 연관관계는 2개로 '회원 객체 -> 팀 객체 단방향 연관관계'와 '팀 객체 -> 회원 객체 단방향 연관관계'가 있고, 테이블 기준 연관관계는 '회원 테이블 <-> 팀 테이블 양방향 연관관계'다. 객체에서도 양방향 연관관계로 얘기하고 있지만, 사실은 서로 다른 단방향 연관관계 2개인 것이다. 객체를 양방향으로 참조하려면 이런식으로 만들어야한다. 하지만 테이블은 외래키 하나로 연관관계를 관리할 수 있다.
연관관계에는 주인(Owner)가 필요하다. 객체의 두 관계 중 하나를 연관관계의 주인으로 지정하는데, 주인만 외래키를 관리(등록, 수정)하고 주인이 아닌 쪽은 읽기만 가능하다. mappedBy
는 주인이 아닌 쪽이 주인을 지정하기 위해 사용한다. 주인의 선정은 일반적으로 외래키가 있는 곳을 주인으로 한다. 또한 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하는 것이 좋다(연관관계 편의 메소드 등 사용).
단방향 매핑만으로도 이미 연관관계 매핑은 완료된다. 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐이다. JPQL 사용시에도 역방향으로 탐색을 할 일이 많다. 기본적으로 단방향 매핑을 잘 하고, 양방향 매핑을 필요할 때 추가해도 된다.
https://github.com/hongbre/jpa-basic/tree/main
GitHub - hongbre/jpa-basic
Contribute to hongbre/jpa-basic development by creating an account on GitHub.
github.com
'스터디 > JPA' 카테고리의 다른 글
JPA 스터디2 - 4 (3) | 2025.08.03 |
---|---|
JPA 스터디2 - 3 (3) | 2025.07.30 |
JPA 스터디2 - 2 (2) | 2025.07.14 |
JPA 스터디2 - 1 (1) | 2025.07.13 |
JPA 스터디1 - 3 (0) | 2025.07.01 |