Spring Framework/[인프런] Java ORM 표준 프로그래밍 - JPA

Chapter 05. 연관 관계 매핑 기초

계란💕 2022. 9. 5. 16:23

5.1 단방향 연관 관계

 

연관 관계가 필요한 이유

  • 연관 관계가 없으면 계속해서 객체를 끄집어내서 조회해야한다. => 객체 지향의 성격과 동떨어진다.

 

  Ex)

  • jpa-basic 프로젝트의 멤버 클래스
java
열기

 

java
열기

  Note) 실행 결과

  • 테이블 두 개가 생성된다.
  • 여기서 무슨 문제가 있을까?
    • 객체를 테이블에 맞춰서 모델링하면 문제가 있다. 
    • 테이블에 맞춰서 외래 키 값을 그대로 가지고 있기 때문이다.

 

 

  Ex) 문제점 

  • persist()하면 pk가 세팅된 다음 영속 상태가 된다.
java
열기

  Note) 실행 결과

  • 멤버 테이블에 TEAM_ID 값을 그대로 가지고 있다.
    • 즉, 테이블에 맞춰 외래키 값을 그대로 가지고 있다는 문제가 있다.

 

 

  Ex)

java
열기

  Note) 실행 결과

  • persist() 순서대로 팀 쿼리 => 멤버 쿼리 나간다.
  • H2 DB는 내부적으로 sequence를 쓴다.

 

 

객체를 테이블에 맞춰 데이터 중심으로 모델링하면 협력 관계를 만들 수 없다.

  • 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
  • 객체는 참조를 사용해서 연관된 객체를 찾는다.
  • 테이블과 객체 사이에는 이런 큰 간격이 있다.

 

 

  • 객체 지향 모델링 (객체 연관관계 사용)

 

 

  Ex) 객체 지향  모델링

 

  • 멤버 테이블에 다음과 같이 애너테이션을 추가하면 기본키 - 외래키 관계를 매핑한다.
java
열기

 

  • 다음 코드를 추가하면 JPA가 자동으로 team 이라는 PK를 꺼내서 FK로 가져온다.
member.setTeam(team);
Team findTeam = findMember.getTeam();

 

  • persist() 하면 영속성 컨텍스트에 들어간다.
    • 그래서 find() 하면 1차 캐시에서 꺼내올 수 있다. => 다음 예제는 데이터베이스에서 가져오는 방법
java
열기

  Note) 실행 결과

  • 멤버에 대해 getTeam()을 하면 바로 팀 이름이 나오게끔 세팅완료
  • 그러면 이제 객체 지향의 특성을 살릴 수 있다.

 

 

  Ex) 영속성 컨텍스트 말고 데이터베이스에서 가져오려면?

java
열기

  Note) 실행 결과

  • 아까 1차 캐시에서 가져올 때와는 다르다.
    • insert 쿼리 두 개 날아가고 그 다음에 select쿼리 실행된다.
    • 멤버와 팀을 조인해서 실행한다. 

 

 

  Ex) 연관 관계 수정하기

  • 커밋하기 전에 아래 코드를 추가하면 어떤 멤버의 팀을 수정 가능
  • 그럼 DB에 외래 키 값이 update 된다.
java
열기

 

 

 

5.2 양방향 연관 관계와 연관 관계의 주인 (1) - 기본

 

 

 

양방향 연관 관계

  • 테이블 연관관계는 단방향에서의 테이블 연관 관계와 똑같다.
    • 테이블은 왜 전혀 변화가 없을까?
    • 테이블은 외래키 하나로 양방향을 모두 이어준다. 따라서, 테이블에는 방향 개념이 없다.
    • 문제는 객체이다.
      • 따라서, Team에 List members를 넣어줘야 양쪽으로 파악 가능하다.

 

 

객체와 테이블 간에 연관 관계를 맺는 차이

  • 객체 연관 관계: 2개
    • 회원 => 팀 연관관계 1개 (단방향)
    • 팀 => 회원 연관관계 1개 (단방향)
    • 객체의 양방향 관계는 사실 단방향 2개라고 보는 것이 맞다.
  • 테이블 연관 관계: 1개
    • 회원 <=> 팀 연관관계 1개 (양방향)
  • 따라서, 둘 중 하나로 외래키를 관리해야한다.
    • Member의 team이 바뀌었을 때, Team의 members가 바뀌었을 때, 이 둘 중 무엇이 바뀌었을 때, MEMBER 테이블이 업데이트 되어야할까?
    • "연관 관계의 주인"

 

 

연관 관계의 주인(Owner)

  • 양방향 매핑 규칙
    • 객체의 두 관계 중 하나를 연관 관계의 주인으로 지정한다.
    • 연관 관계의 주인만이 외래 키를 관리한다. (관리: 등록, 수정)
    • 주인이 아닌 쪽은 읽기만 가능
    • 주인은  mappedBy 속성을 사용하면 안 된다. 
    • 주인이 아니면 mappedBy 속성으로 주인을 지정한다.
    • 주인이 아닌 쪽에 데이터를 넣어봐야 아무 일도 일어나지 않는다. (오류도 없다.) 단순 조회만 가능
  • 외래 키가 있는 곳주인으로 정한다.(다대일 중 "다"에 해당한다.) 멤버랑 팀 중에 멤버를 말한다.
  • 현재 Member.Team이 연관 관계의 주인이다.

 

 

  Ex) 양방향 연관 관계 - 반대 방향으로 객체 그래프 탐색

  • Team에 다음과 같이 추가한다.
    • mapped by = "team" : 에서의 team은 멤버 클래스의 변수 team을 의미한다.
java
닫기
<hide/>    
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();

 

  • flush(), clear() 해줘야 DB에서 깔끔하게 값을 가져온다.
  • 멤버 => 팀 => 멤버 ... 이를 양방향 연관관계라고 한다.
  • team.getId() 에서 SELECT 쿼리가 실행된다.
java
열기

  Note) 실행 결과

  • List에 세팅한 게 없지만 그래도 출력된다.
  • 왜? JPA에서 

 

  • 위에 코드에서 flush() 위에 아래 코드를 추가해야 문제가 생기지 않는다.
    • 왜? 
    • 완전히 flush(), clear()가 되면 문제가 없다. 하지만? 
team.getMembers().add(member);
  • flush(), clear() 가 없으면 영속성 컨텍스트에 멤버와 아이디가 그대로 들어가 있다.

 

 

※ 중요※  연관 관계의 주인과 mappedBy

  • mappedBy를 알려면 객체와 테이블 간에 연관 관계를 맺는 차이를 이해해야한다.

 

 

5.3 양방향 연관 관계와 연관 관계의 주인 (2) - 주의점, 정리

 

  Ex) 양방향 연관 관계 

java
열기

 

  • 연관관계의 주인에 값을 입력하지 않으면 아무 일도 일어나지 않는다.
//            team.getMembers().add(member);    // 읽기 전용이므로 쿼리 날라가지도 않는다.

 

  Note) 실행 결과

  • 팀 아이디가 null이다 왜그럴까?
  • 연관 관계의 주인은 멤버이지 팀이 아니다.

 

  • 따라서, 다음과 같이 코드를 수정해야한다.
    • member.setTeam() 을 추가한다. => changeTeam()
java
열기

  Note) 실행 결과 - 다음과 같이 팀 아이디가 잘 들어간다.

  • 결론: 순수힌 객체 관계를 고려하면 항상 양 쪽에 값을 입력해야한다.
  • 양쪽에 모두 add() 코드를 넣어준다.

 

 

  Ex) 연관 관계 편의 메서드 생성

  • Mermber 클래스에 team에 대한 내용을 다음과 같이 추가할 수도 있다.
    • 이렇게 하면 하나만 호출해도 양쪽의 값을 모두 가져올 수 있다.
java
열기
  • 그리고 매인 클래스의 team.getMembers().add() ... 이 부분은 삭제하도록 한다.
java
열기

  Note) 실행 결과

 

 

 

양방향 연관 관계 주의

  • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정한다.
  • 연관관계 편의 메서드를 생성한다.
  • 양방향 매핑 시에 무한 루프를 조심한다.
    • ex) toString() lombok, JSON 생성 라이브러리(컨트롤러에서 response로 엔티티를 보내버리면 엔티티가 가진 연관 관계가  양방향일 때)
    • 컨트롤러에는 엔티티를 반환하지 않도록 한다.

 

 

양방향 매핑 정리

  • 단방향 매핑만으로 이미 연관관계 매핑은 완료
  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐이다.
  • JPQL에서 역방향으로 탐색할 일이 많다.
  • 우선, 단방향 매핑을 잘 해두고 양방향은 나중에 필요할 때 추가해도된다. (테이블에 영향을 주지 않기 때문이다.)

 

 

 

5.4 실전 예제 2 - 연관 관계 매핑 시작

테이블 구조
객체 구조 - 참조를 사용하도록 변경

 

  Ex)

  • Order 테이블
java
열기

 

  • OrderItem 클래스가 "다"에 해당한다. (하나의 Order에 여러 개의 OrderItem이 있기 때문)

 

  • OrderItem 클래스
    • 두 개의 필드(itemId, orderId)를 지운다.
    • 이렇게 하면 외래 키 값을 가져오는 것이 아니라 order, item이라는 객체를 가진다.
    • 따라서 getOrder(), getItem()이 가능해진다.
java
열기

  Note) 실행 결과

객체 구조 - 참조를 사용하도록 변경

  • 위  그림에서 굵게 표시된 필드는 반대 방향을 참조할 가능성이 있는 것들이다.

 

 

  Ex)

  • Member
java
닫기
<hide/
package jpabook.jpashop.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;
    private  String name;
    private String city;
    private String street;
    private String zipcode;

    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();
    
    public Long getId() {return id;
    }

    public void setId(Long id) {this.id = id;
    }

    public String getName() {return name;
    }

    public void setName(String name) {this.name = name;
    }

    public String getCity() {return city;
    }

    public void setCity(String city) {this.city = city;
    }

    public String getStreet() {return street;
    }

    public void setStreet(String street) {this.street = street;
    }

    public String getZipcode() {return zipcode;
    }

    public void setZipcode(String zipcode) {this.zipcode = zipcode;
    }
}
  • Order
    • 연관 관계 편의 메서드 addOrderitem() 를 만든다.
java
열기

 

  • Main
java
열기

  Note) 실행 결과

 

 

 

 

출처 - https://www.inflearn.com/course/ORM-JPA-Basic

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com