Spring/[인프런 김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편]

[인프런 김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편] 연관관계 매핑 기초

h2boom 2024. 8. 20. 21:35

연관관계 매핑

  • 객체를 테이블에 맞춰 설계하는 경우 - 객체를 데이터 중심으로 설계하면 협력 관계를 만들 수 없다.
    • 테이블은 외래 키로 조인을 해서 연관된 테이블을 찾는다.
    • 객체는 참조를 사용해서 연관된 객체를 찾는다.

단방향 연관관계

@Entity
public class Member {
	//내용 생략

	@ManyToOne
	@JoinColumn(name = "TEAM_ID")
	private Team team;
    
    ...
}
  • 단방향 연관관계 예제
    • Team과 Member가 1:N 관계라고 가정할 때
      • 객체는 참조를 사용하기 때문에 Team 객체를 Member에 포함
        • Team과 Member는 일대다 관계이기 때문에 Team 객체에 @ManyToOne으로 명시
        • @JoinColumn(name = "외래키명")으로 외래 키와 매핑해줘야 한다.
      • 테이블은 외래 키로 조인을 해서 연관 테이블을 찾기 때문에 Team의 Id 값을 Member에 포함
      • 이 경우 객체 Member에서 Team을 참조할 수는 있지만 Team에서 Member 객체를 참조할 수 없다.


양방향 연관관계 / 연관관계의 주인

  • 단방향 연관관계에서 양방향 연관관계로 변해도 테이블 연관관계는 변하지 않는다.
    • 객체는 단방향 연관관계인 경우 양쪽 모두 알 수 없고 한쪽만 아는 관계이다.
  • 테이블은 단방향, 양방향이라는 개념이 따로 존재하지 않고 외래 키로 서로의 연관 관계를 알 수 있다. => JOIN

 

//양방향 연관관계

@Entity
public class Member {
	//내용 생략

	@ManyToOne
	@JoinColumn(name = "TEAM_ID")
	private Team team;
    
    ...
}

@Entity
public class Team {
	//내용 생략
    
	@OneToMany(mappedBy = "team")
	private List<Member> members = new ArrayList<>();
    
	...
}
  • 양방향 연관관계 예제
    • Team과 Member가 1:N 관계라고 가정할 때
      • 1개의 Team에 여러 Member가 포함될 수 있기에 Team 클래스에서 Member List 객체에 @OneToMany로 명시해줘야 한다.
        • @OneToMany(mappedBy = "객체명") => 자신과 연관관계에 있는 상대 클래스에서 사용되는 자신의 참조 객체명을 적어준다.

 

 

  • 객체와 테이블이 관계를 맺는 차이
    • 객체 연관관계 = 2개
      • ex) Member -> Team 연관관계 1개 (단방향)
        Team -> Member 연관관계 1개 (단방향)
    • 테이블 연관관계 = 1개
      • Member <-> Team 연관관계 1개 (양방향)
  • 객체의 양방향 관계는 양방향 관계가 아닌 서로 다른 단방향 관계 2개이다.
    • 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야한다.
  • 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.
    • ex) MEMBER.TEAM_ID 외래 키 하나로 MEMBER와 TEAM 테이블의 양방향 연관관계를 가진다.
      (양쪽으로 조인 가능)

 

  • 객체의 양방향 관계에서 외래 키를 관리할 때 둘 중 하나로 관리해야한다. ( = 연관관계의 주인)

 

  • 연관관계의 주인
    • 양방향 매핑 규칙
      • 객체의 두 관계중 하나를 연관관계의 주인으로 지정
      • 연관관계의 주인만 외래 키를 관리 (등록, 수정)하고 나머지는 읽기만 가능
      • 주인은 mappedBy 속성을 사용하지 않고 주인이 아니면 mappedBy 속성으로 주인을 지정한다.
    • 비지니스 로직을 기준으로 주인을 선택하는 것이 아닌 외래 키가 있는 곳을 기준으로 주인으로 정해야한다.
      (정해진 것은 아니지만 김영한님의 추천)
      • 이 방식을 사용해야 엔티티와 테이블이 매핑이 된다.
        • ex) 만약 반대로 Team.members를 연관관계 주인으로 한다면 Team을 수정했을 때 Member 테이블의 UPDATE 쿼리가 날라가게 되어 혼동이 있을 수 있다. 
      • 테이블의 입장에서는 외래 키가 있는 곳이 일대다 관계에서 N이며 N쪽이 연관관계의 주인이 되는 것.
      • Member - Team 양방향 예제에서는 Member.team이 연관관계의 주인이다.
        • ex) member.setTeam()으로 Team을 지정해줘야하고 만약 team.getMembers().add()로 Member를 지정해준다면 DB에 반영되지 않는다.
          => JPA에서는 List members는 읽기 전용이기 때문에 DB에 반영하지 않는다.

 

  • 양방향 매핑시 가장 많이 하는 실수
    • 연관관계 주인에 값을 입력하지 않는 것.
      = 역방향만 연관관계 설정해주는 것.

 

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);

// 두 가지를 모두 생략하면 예상과 다른 결과가 나온다.
em.flush(); 
em.clear();

Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
  • 위 예제 코드에서 em.flush(), clear()가 없다면?
    1. em.find()로 findTeam을 조회할 때 1차 캐시에서 조회하게 된다.
    2. 1차 캐시 내부에는 member.setTeam()을 통해 Team에 member가 추가되어 있지 않다.
    3. findTeam.getMembers()를 하더라도 아무 member도 없게 된다.
  • 위와 같은 상황과 테스트 케이스 작성 시 상황에서는 순수한 객체 관계를 고려해서 항상 양방향 매핑 시에는 되도록 양쪽 모두에 값을 넣어줘야 한다!!
    • 연관관계 편의 메소드를 생성해서 사용하면 좋다.
@Entity
public class Member {
	//생략
	
	//연관관계 편의 메소드
	public void setTeam(Team team) {
		this.team = team;
		team.getMembers().add(this);
	}
    
}

 

  • 양방향 매핑시 무한 루프 조심할 것
    • ex) toString(), Lombok, JSON 생성 라이브러리...
    • 해결 방법
      • Lombok 사용시 toString() 기능은 제외하고 사용하는 것이 좋다.
      • Controller에서 Entity를 절대 반환하면 안된다.
        • JSON 생성 라이브러리에 의해서 무한 루프가 생길 수 있다.
        • Entity가 수정될 수 있으며 수정되면 API 스펙이 바뀌기에 사용하는 입장에서 난처해진다.

 

  • 양방향 매핑 내용 정리
    • 단방향 매핑만으로도 이미 연관관계 매핑은 완료된다.
      • 실무에서 설계 시 양방향 매핑을 사용하지 않고 단방향 매핑으로 설계를 끝내야한다.
      • 양방향 매핑은 나중에 JPQL에서 역방향 탐색할 때 필요하면 추가해서 사용하면 된다. (테이블에는 영향 x)
    • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가만 된 것일 뿐이다.
    • 가능하면 최대한 단방향 매핑으로만 잘 설계하는 것이 가장 중요하며 양방향은 JPQL을 사용할 때 필요한 경우가 많기에 필요할 때 나중에 추가해라.

출처: [인프런 김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편]

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

 

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

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

www.inflearn.com