study/웹개발 스터디 백엔드 EFUB 💻

[JPA] 5장 - 연관관계 매핑 기초

은솜솜솜 2025. 4. 3. 15:54
728x90

 

본 글은 도서 '자바 ORM 표준 JPA 프로그래밍'의 5장 요약 정리본입니다.

 

 

객체 : 참조(주소) 로 관계 맺고 / 테이블 : 외래 키로 관계 맺음. → 완전히 다름 !!

객체 연관관계와 테이블 연관관계를 매핑하는 일이 객체 관계 매핑에서 가장 어려운 부분.

객체의 참조와 테이블의 외래키를 매핑하는 것이 5장의 목표 !

 

⭐ 핵심 키워드

방향 : 단방향(한쪽만 참조) / 양방향(서로 모두 참조; 테이블은 모두 양방향 by 외래키)

다중성 : 다대일, 일대다, 일대일, 다대다

연관관계의 주인 : 객체를 양방향 연관관계로 만들면, 연관관계의 주인을 정해야함 !

 


 

 

 

✳️ 단방향 연관관계

 

1️⃣객체 연관관계

Member.team : 회원 객체가 팀 객체를 참조. (단방향)

member.getTeam()으로 회원의 팀 조회 가능. but, 팀에서 회원으로 접근하는 필드는 없다

 

*객체 그래프 탐색 ; A.getB() 이런식으로 참조를 사용해 연관관계를 탐색할 수 있다.

 

2️⃣ 테이블 연관관계

회원 테이블의 외래키 : Team_Id (양방향)

서로 조인 가능 ! member join team, team join member 둘 다 가능

 

*조인 ; 외래 키를 사용해 연관관계 탐색

SELECT *
FROM TableA A
LEFT JOIN TableB B ON A.id = B.a_id;

 

 

객체 연관관계와 테이블 연관관계의 차이점

  • 참조는 언제나 필드를 사용한 단방향.
    서로 필드를 추가해 참조를 보관해도, 양방향이 아니라 서로 다른 단방향 관계가 2개인 것. 
  • 테이블은 외래 키 하나로 양방향 조인.

 

연관관계 매핑 어노테이션

@ManyToOne : 다대일 관계 매핑 (필수)

@JoinColumn(name="외래키이") : 외래 키 매핑 (생략가능)

 

 


 

 

✳️ 연관관계 사용

 

1️⃣ 저장

+ JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 한다.

// 팀 1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
        
// 회원1 저장
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1); // 연관관계 설정 ; 회원->팀 참조
em.persist (member1); // 저장

JPA는 참조한 팀의 식별자를 외래 키로 사용해 등록 쿼리 자동 생성함 !

 

 

2️⃣ 조회

1. 객체 그래프 탐색 (객체 연관관계 사용)

B.getA() 이런식으로 객체를 통해 연관된 엔티티를 조회

 

2. 객체지향 쿼리 사용 (JPQL)

String jpql = "select m from Member m join m.team t where " +
	"t.name =:teamName";
        
List<Member> resultList = em.createQuery(jpql, Member.class)
	.setParameter("teamName", "팀1")
	.getResultList();

 

 

3️⃣ 수정

Team team2 = new Team("team2", "팀2");
em.persist(team2);

Member member = em.find(Member.class, "member1");
member.setTeam(team2);	//참조하는 team1을 team2로 변경

팀1 소속이던 회원을 새로 팀2에 소속하도록 수정.

 

4️⃣ 연관관계 제거

Member member1 = em.find(Member.class, "member1");
member1.setTeam(null);	// 연관관계 제거

연관관계 null로 설정해서 제거.

 

+ 외래 키 제약조건으로 DB오류가 발생하니, 연관된 엔티티를 삭제하려면 연관관계를 먼저 제거해야함 !

 

 


 

 

✳️ 양방향 연관관계

 

양방향 매핑하기

// 추가
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<Member>();

 팀 엔티티 수정 ! ( Team 엔티티에 속한 Member 객체들의 리스트를 저장하는 필드 추가 )

 

 

 

 


 

 

✳️ 연관관계의 주인 - mappedBy

외래 키 관리자를 선택하는 것.

 

연관관계의 주인만이 DB 연관관계와 매핑되고, 외래 키를 관리할 수 있다. (주인 아닌 쪽은 읽기만 가능)

 

테이블은 외래 키 하나로 두 테이블의 연관관계를 관리하지만,

엔티티를 양방향 연관관계로 설정하면, 객체의 참조는 둘, 테이블 외래 키는 하나라는 차이가 발생.

따라서 두 객체 연관관계 중 하나를 정해 테이블의 외래 키를 관리하도록 함 → 연관관계의 주인

 

테이블에 외래 키가 있는 곳으로 연관관계의 주인을 정해야한다.

다대일, 일대다는 항상 다 쪽이 외래 키 가짐. @MantToOne은 mappBy 설정할 필요 없으므로 속성 존재 x 

 

 


 

✳️ 양방향 연관관계 저장

 

team1.getMembers().add(member1);	//무시(연관관계의 주인이 아님)
team1.getMembers().add(member2);	//무시(연관관계의 주인이 아님)

 

*add() : 리스트에 객체를 추가

 

연관관계의 주인인 Member.team이 다음과 같이 외래 키를 관리.

member1.setTeam(team1);	//연관관계 설정(연관관계의 주인)
member2.setTeam(team1);	//연관관계 설정(연관관계의 주인)

 


 

 

✳️ 양방향 연관관계의 주의점

 

  team1.getMembers().add(member1);	//무시(연관관계의 주인이 아님)
  team1.getMembers().add(member2);	//무시(연관관계의 주인이 아님)
  
  //member1.setTeam(team1);	//연관관계 설정(연관관계의 주인)
  //member2.setTeam(team1);	//연관관계 설정(연관관계의 주인)

 

연관관계의 주인이 아닌 곳(예: Team.members)에만 값을 넣으면, 외래 키가 null로 설정될 수 있다.
객체 관점에서 team1.getMembers().size()는 올바르게 값을 반환하지 않음

따라서 양쪽 모두에 값을 설정하는 것이 중요 `

 

연관관계 편의 메서드

이처럼 양방향 연관관계는 양쪽 다 신경을 써야 하는데 각각 코드를 호출하면 실수로 한 곳에만 값을 넣어줄 수도

이를 방지하기 위해 다음과 같이 set을 수정하면 좋음

public class Member {
  
  private Team team;
  
  public void setTeam(Team team) {
  	this.team = team;
	team.getMembers().add(this);
  }
  ...

 

 

 

public void test() {
  
  Team team1 = new Team("team1", "팀1");
  em.persist(team1);
  
  Member member1 = new Member("member1", "회원1");
  member1.setTeam(team1);	// 양방향 설정
  em.persist(member1);
  
  Member member2 = new Member("member2", "회원2");
  member2.setTeam(team1);	// 양방향 설정
  em.persist(member2);
}

 

연관관계 편의 메서드 주의사항

member1의 Team을 수정했을 때 기존 팀에서 여전히 member1이 조회되는 문제.

  member1.setTeam(teamA)	//teamA
  member1.setTeam(teamB)	//teamA -> teamB 수정
  Member findMember = teamA.getMember();	//member1이 여전히 조회된다.

 

연관관계를 수정할 때, 기존 연관관계를 제거하지 않아서 발생.

다음과 같이 수정.

public void setTeam(Team team) {
  if (this.team != null) {
  	this.team.getMembers().remove(this);
  }
  this.team = team;
  team.getMembers().add(this);

 

 

728x90