JPA
[JPA] 연관 관계 매핑 기초2 (양방향)
나는시화
2024. 1. 9. 17:02
예제 시나리오(양방향)
- 회원과 팀이 있다.
- 회원은 하나의 팀에만 소속될 수 있다.
- 회원과 팀은 다대일 관계다.
Member Entity
- Member Entity는 바뀐 것이 없다.
package jbabook.jpa.shop.domain;
import jakarta.persistence.*;
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team; // Team Entity와 조인
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Team Entity
- @OneToMany 어노테이션을 사용
- mappedBy = "" 사용해주어야함. (Member Entity에서 Team이 어떤 변수명으로 사용되고 있는지 매핑.)
- 연관관계 주인(양방향 매핑 규칙)
- 다(Member) 쪽에서 외래 키를 관리해주어야 함.
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계 주인만이 외래 키를 관리(등록, 수정)
- 주인이 아닌 쪽은 읽기만 가능
- 주인은 mappedBy 속성 사용X
- 주인이 아니라면 mappedBy 속성으로 주인 지정
package jbabook.jpa.shop.domain;
import jakarta.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
@Column(name = "TEAM_NAME")
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public List<Member> getMembers() {
return members;
}
}
package jbabook.jpa.shop;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;
import jbabook.jpa.shop.domain.Member;
import jbabook.jpa.shop.domain.Team;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Team team = new Team();
team.setName("teamName1");
em.persist(team);
Member member = new Member();
member.setUsername("name1");
Member member2 = new Member();
member2.setUsername("name2");
member.setTeam(team);
member2.setTeam(team);
em.persist(member);
em.persist(member2);
em.flush();
em.clear();
Member findMember = em.find(Member.class,member.getId());
List<Member> members = findMember.getTeam().getMembers();
for(Member mem : members){
System.out.println("members name = " + mem.getUsername());
}
tx.commit();
} catch (Exception e){
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
member -> team , team -> membber 양방향에서 데이터 조회 가능
주의!
- 웬만하면 단방향으로 설계하기
- member.setTeam() 메서드로 연관관계 주인한테만 데이터를 집어넣어도 DB에서 가져올 때는 정상적으로 가져와짐.
- DB에서 가져오는 것이 아닌 1차 캐시에서 가지고 올 때는 빈 배열을 가지고옴.
- 따라서 객체지향적으로 셋팅하기 위해선 데이터를 양쪽에 넣어주는 게 맞음.
- 예) member.setTeam(team); team.getMembers().add(member);
package jbabook.jpa.shop;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;
import jbabook.jpa.shop.domain.Member;
import jbabook.jpa.shop.domain.Team;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Team team = new Team();
team.setName("teamName1");
em.persist(team);
Member member = new Member();
member.setUsername("name1");
em.persist(member);
team.getMembers(); // 빈 배열
team.getMembers().add(member); // 이걸 안 넣어주면 객체답지가 않음. 또한 em.flush()와 em.clear(); 가 없어서 데이터가 출력되지 않음.
Team findTeam = em.find(Team.class,team.getId());
List<Member> members = findTeam.getMembers();
for(Member mem : members){
System.out.println("mem.getUsername = " + mem.getUsername());
}
tx.commit();
} catch (Exception e){
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
아래는 연관관계 편의 메서드를 생성한 것.(Member Entity의 setTeam() 메서드)
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
양방향 매핑 정리
- 단방퍙 매핑만으로도 이미 연관관계 매핑은 완료
- 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐
- JPQL에서 역방향으로 탐색할 일이 많음
- 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 됨(테이블에 영향을 주지 않음)