이 글은 김영한님의 JPA 강의 중 5장을 듣고 정리한 내용입니다 :)
강의 : 자바 ORM 표준 JPA 프로그래밍 - 기본편
교재 : 자바 ORM 표준 JPA 프로그래밍🤷♀️
IF) 회원과 팀은 다대일관계를 가짐! 이때, 객체를 관계형 DB에 맞춰 모델링하면
테이블은 외래 키로 조인해서 찾는 반면, 객체는 참조로 연관 객체를 찾음
⇒ 패러다임의 차이!
(SQL : select * from member a join a team t on a.team_id = t.team_id; )
// 맴버 엔티티
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "TEAM_ID")
**private Long teamId;** // 객체지향 스럽지않음! 관계형DB에 맞춘 방식
****...
}
// 팀 엔티티
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
…
}
// ------------ 연관 관계 매핑 ------------
// 팀 저장
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());
//member랑 team의 연관관계가 없음!
Team findTeam = em.find(Team.class, team.getId());
// 맴버 엔티티
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
//@Column(name = "TEAM_ID")
//private Long teamId; // 객체지향스럽지않음! 관계형DB에 맞춘 방식
@ManyToOne
@JoinColumn(name = "TEAM_ID")
**private Team team;** // 객체 지향스러운 방법!
****...
}
// -------------조회-------------
Member member = em.find(Member.class, member.getId);
Team team = member.getTeam();
// -------------수정-------------
Team newTeam = member.getTeam();
member.setTeam(newTeam); // 팀 수정!
1차 캐시에 있어서 쿼리 날라가는 게 안 보일때? 근데 쿼리를 보고 싶다면!
em.persist(member); // 영속성 컨텍스트에 있음
Team findTeam = em.findMember.getTeam(); //쿼리 안나가고 1차 캐시에서 가져옴
em.flush(); // 그냥 빨리 1차 캐시에 있는 거 강제로 디비에 보내기
em.clear(); // 영속성 컨텍스트(1차캐시) 클리어하면 쿼리는 이제 새롭게 나감!
: 양쪽으로 참조해서 서로 조회할 수 있는 관계
다대일 관계(Member)에는 @ManyToOne만! (단방향과 동일한 방법)
일대다관계(Team)에는 @OneToMany
그리고 컬렉션 추가해주고, mapped by에는 해당 다대일이 매핑되어있는 변수명 넣어주기
//멤버 엔티티
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@ManyToOne // 다대일관계 매핑(단방향 연관관계와 동일한 방법)
@JoinColumn(name = "TEAM_ID")
private Team team;
…
}
//팀 엔티티
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "team") // 일대다관계 매핑
List<Member> members = new ArrayList<Member>();
//(컬렉션 추가해주기, mapped by에는 해당 다대일이 매핑되어있는 변수명!)
…
}
// 한 멤버가 속한 팀의 전체 멤버 리스트도 받아올 수 있음
List<Memeber> members = member.getTeam().getMembers();
IF ) 멤버가 속한 팀을 바꾸고 싶다면,
팀에서 멤버를 바꿔야될까, 아니면 멤버에서 팀을 바꿔야 될까?
⇒ DB 입장에서는 멤버에서 팀아이디인 FK값만 바꾸면 된다!
⇒ 그러면 객체 입장에서는 ??
2개의 단방향 관계로 억지로 만든 양방향관계인데, 무슨 값을 바꿔야 하나?!
⇒ 그래서 양방향 연관관계의 주인을 정해서, 주인이 FK를 관리하게끔 해보자!
그리고 주인이 아닌 쪽(mapped by로 주인을 걸어줌)은 읽기만 가능하게 하자!
주인은 외래키가 있는 곳으로 정하자 즉, '다'쪽(Member)을 주인으로 하자
반대로 하면 헷갈리고 성능 이슈가 있음!, 쿼리가 반대쪽으로 나가면 헷갈리니까
: 주인이 아닌쪽에만 매핑하면 매핑이 안됨!
: 객체지향적인 관점에서는 양방향에서 다 매핑해주어야 안전
(단, 처음에는 주인쪽에서만 매핑 후 필요시 양방향 매핑 → 아래 설명)
실습 ) 연관관계 매핑은 주인인 쪽에서는 꼭 해주어야하고, 만약 역방향에서만 하면 설정 안됨
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
member.setTeam(team); // 이렇게 주인인 쪽에서 연관관계를 설정해주어야함!
// 역방향(주인이 아닌 방향)만 연관관계 설정 => 이것만 하면 설정안됨
team.getMembers().add(member);
em.persist(member);
//멤버 엔티티 ('다'쪽, 주인!)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
…
}
//연관관계 편의 메소드
public void changeTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
⇒ 가능하면 단방향으로 하되, 실무에서 양쪽으로 조회가 필요한 경우에 양방향 매핑을 하자!
(양방향은 복잡하니까 필요시에 매핑하자)