DB에서 가장 핵심이 되는 연관관계 매핑
예제 시나리오
- 회원(Member)과 팀(Team)이 존재한다.
- 회원은 하나의 팀에만 소속될 수 있다.
- 회원과 팀은 다대일 관계이다.
초기 코드 설계
Member 클래스
@Entity
@Setter
@Getter
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;
}
Team 클래스
@Entity
@Getter
@Setter
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
}
JpaMain 클래스
try {
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
// 위에서 em.persist하면서 insert가 됐으므로 id를 꺼낼 수 있음
member.setTeamId(team.getId());
em.persist(member);
tx.commit(); // 성공하면 커밋
}
위 코드의 문제점
- 이 코드는 객체지향적이지 않다.
- 외래키를 직접 다루고 있어서 데이터 중심의 설계이다.
- 이렇게 설계하면 객체 간의 협력 관계를 만들기 어렵다.
전 글에서 Id가 자동으로 생성되었을 때 em.persist하면 insert query가 나가는 것을 학습했다.
그러므로 setTeamId를 할 때 team.getId()를 사용할 수 있다.
하지만 이렇게 하는게 과연 객체지향일까?
H2 DB에서 join을 해보자.
TEAM_ID가 2번 나오는 것을 볼 수 있다. 비정상적인 상태이다.
외래키를 직접 다루고 있는 것이다.
객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.
테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
객체는 참조를 사용해서 연관된 객체를 찾는다.
테이블과 객체 사이에는 이런 큰 간격이 있다.
Member 클래스
@Entity
@Setter
@Getter
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;
}
개선된 코드의 장점
- Member에서 Team을 직접 참조할 수 있다.
- 개체 지향 적인 설계가 가능하다.
- 직접 참조하기 때문에, Member에서 Team을 직접 꺼낼 수 있다.
JPA Main
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);
Member findMember = em.find(Member.class, member.getId());
Team findTeam = findMember.getTeam();
System.out.println("findTeam = " + findTeam);
실행 결과
findTeam = reviewjpa.Team@e84fb85
Hibernate:
/* insert reviewjpa.Team
*/ insert
into
Team
(name, TEAM_ID)
values
(?, ?)
Hibernate:
/* insert reviewjpa.Member
*/ insert
into
Member
(TEAM_ID, USERNAME, MEMBER_ID)
values
(?, ?, ?)
쿼리 최적화
지금 select query가 없는 이유는 무엇일까?
방금 em.persist하며 member와 team을 커밋하기 전 영속성 컨텍스트에, 1차 캐시에 저장되어있기 때문이다.
하지만 DB에서 직접 갖고오는 query를 보고 싶다면
em.flush();
em.clear();
이렇게 하게되면 영속성 컨텍스트, 1차 캐시를 전부 비우는 것이므로 쿼리 결과가 나오게 된다.
다음에는 양방향 연관관계와 연관관계의 주인에 대해 알아본다. JPA에 가장 중요한 부분이다.
'JPA > ORM 표준 JPA' 카테고리의 다른 글
[ORM 표준 JPA] 8.다양한 연관관계 매핑 (0) | 2023.09.30 |
---|---|
[ORM 표준 JPA] 7. 연관관계 매핑2 양방향 연관관계와 연관관계의 주인 (0) | 2023.09.23 |
[ORM 표준 JPA] 5. 엔티티 매핑2 - 기본키 전략 (0) | 2023.09.20 |
[ORM 표준 JPA] 4. 엔티티 매핑1 - 객체와 테이블 매핑 (0) | 2023.09.18 |
[ORM 표준 JPA] 3. 영속성 관리 - 내부 동작 방식 (0) | 2023.09.16 |