해당 시리즈는 김영한님의 JPA 로드맵을 따라 학습하면서 내용을 정리하는 글입니다
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
...
}
@Entity
public class Order {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
@Column(name = "member_id")
private Long memberId;
...
}
// 찾은 주문 객체에서
Order order = em.find(Order.class, 1L);
// 주문한 멤버 객체의 아이디 값을 받아와서
Long memberId = order.getMemberId();
// 그 아이디 값으로 멤버를 찾아내야 한다
Member findMember = em.find(Member.class, memberId);
Member
객체를 불러올 수 있습니다@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
...
}
@Entity
public class Order {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
private Member member;
...
}
// 찾은 주문 객체에서
Order order = em.find(Order.class, 1L);
// 바로 그 주문을 한 멤버를 불러올 수 있습니다
Member findMember = order.getMember();
UML
도 잘못됩니다UML 이란?
Unified Modeling Language
의 약자입니다
UML은 개발자들이 실제 개발단계에 들어가기 전에 다이어그램을 통해서 프로그램의전체적인 설계
,필요한 변수
,함수
등을 정하는 등 전반적으로 직접 코딩하기 전 계획을 시각화한 것입니다
@Entity
public class Member {
...
@ManyToOne // 이 관계를 표현하는 애노테이션
@JoinColumn(name = "TEAM_ID") // 이 객체가 데이터베이스의 어떤 컬럼과 연관관계를 맺는지
private Team team;
...
}
Member
클래스에만 Team
에 대한 관계를 설정해 줬기 때문에 Member
에서만 Team
의 정보를 알 수 있다는 차이점이 발생합니다1:N
의 관계에서 1
에 해당하는 객체에도 관계에 대해서 알려줘야 한다는 점입니다...
// Team 입장에서의 관계를 표현하는 애노테이션과
// 어떤 필드와 관계를 맺고 있는지 관계 대상이 되는 필드의 이름을 적어주어야 합니다
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
// add 할 때 NPE를 방지하기 위해서 관례적으로 ArrayList로 많이 초기화 합니다
...
MEMBER.TEAM_ID
외래 키 하나로 양방향 연관관계를 가지게 됩니다(양쪽으로 조인할 수 있습니다)team
, members
) 중 하나를 데이터베이스의 외래키와 연관관계를 매핑하는 데 있어서 어떤 걸 연결해야 하는지 고민을 하게 됩니다mappedBy
속성을 사용하지 않습니다mappedBy
속성으로 주인을 지정합니다Member.team
이 연관관계의 주인입니다Team team = new Team();
team.setName("TeaA");
em.persist(team);
Member member = new Member();
member.setName("member1");
// 역방향(주인이 아닌 쪽)만 연관관계 설정
team.getMembers().add(member);
em.persist(member);
Team team = new Team();
team.setName("TeaA");
em.persist(team);
Member member = new Member();
member.setName("member1");
// 연관관계 주인에 값 설정
member.setTeam(team);
em.persist(member);
Team team = new Team();
team.setName("TeaA");
em.persist(team);
Member member = new Member();
member.setName("member1");
// 연관관계 주인에 값 설정
member.setTeam(team);
em.flush();
em.clear();
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
for (Member m : members) {
System.out.println("m = " + m.getUsername());
}
Member
의 리스트를 반환해주며 아주 잘 작동합니다em.flush()
, em.clear()
부분이 없다면 어떻게 될까요?findTeam
은 데이터베이스가 아닌 em.persist(team)
일 때 1차 캐시에 저장된 그 Team
객체가 나오게 됩니다Team
객체안에는 Member
의 리스트가 없는 상태인 빈 껍데기입니다Team team = new Team();
team.setName("TeaA");
em.persist(team);
Member member = new Member();
member.setName("member1");
// 연관관계 주인에 값 설정
member.setTeam(team);
team.getMembers().add(member);
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
for (Member m : members) {
System.out.println("m = " + m.getUsername());
}
// 예를 들면 Member 클래스에서 setTeam이라는 setter 메서드 안에 설정을 합니다
public void setTeam(Team team) {
this.team = team;
// 여기에 설정해두면 주인 쪽에서 값을 넣을 때 자동으로 맺어지니 실수할 일이 줄어들겠죠?
team.getMembers().add(this);
}
// 아니면 Team 클래스에서 설정하도록 하실 수도 있습니다
public void addMember(Member member) {
member.setTeam(this);
members.add(member);
}
toString()
, lombok
, JSON 생성 라이브러리
Stackoverflow
에 빠지게 됩니다lombok
의 toString
기능을 사용하는 것은 주의하거나 지양하도록 하고, 써야 한다면 한쪽에서 끊는 옵션을 설정해서 사용하도록 해야 합니다JSON 생성 라이브러리
의 경우에는 애초에 Entity
자체를 JSON
값으로 리턴하는 것을 하지 않으면 됩니다. 엔티티의 경우 언제든 변경될 가능성이 있고, 그 경우 API 스펙 자체가 변경되는 등 여러 파생된 문제점들이 퍼지기 때문에 꼭 필요한 명세만 기록된 DTO
나 Response
등을 만들어서 사용하도록 합시다JPQL
에서 역방향으로 탐색할 경우가 발생합니다