
해당 시리즈는 김영한님의 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에서 역방향으로 탐색할 경우가 발생합니다