BACK/JPA

[JPA] TIL 7일차 - JPA 다양한 연관관계 매핑

연듀 2024. 4. 3. 20:23

 

연관관계 매핑시 고려사항 3가지

  1. 다중성
    1. 다대일 @ManyToOne
    2. 일대다 @OneToMany
    3. 일대일 @OneToOne
    4. 다대다 @ManyToMany: 실무에서 쓰면 안됨
  2. 단방향, 양방향
    1. 테이블: 외래 키 하나로 양쪽 조인이 가능해 방향의 개념이 없다.
    2. 객체: 참조용 필드가 있는 쪽으로만 참조가 가능하다.
    3. 한쪽만 참조하면 단방향, 양쪽이 서로 참조하면 양방향(단방향이 두개)
  3. 양방향 시 연관관계의 주인
    • 연관관계의 주인: 외래 키를 관리하는 참조
    • 주인의 반대편: 외래 키에 영향을 주지 않고 단순 조회만 가능
  4. 객체 양방향 관계는 참조가 두군데 있고, 둘 중 테이블의 외래키를 관리할 곳을 지정해야 한다.

 


다대일

 

다대일 단방향

관계형 DB는 다쪽에 외래키가 항상 있어야 한다.

외래키가 있는 객체에 참조를 넣어 주고 매핑을 걸면 된다.

  • 가장 많이 사용하는 연관관계

 

Member

@ManyToOne // (Member : Team = N : 1)
@JoinColumn(name="TEAM_ID") // join 하는 컬럼. team과 외래키 연관관계 매핑
private Team team;

 

 

다대일 양방향

 

다(외래키가 있는 쪽)가 연관관계의 주인

반대쪽 객체에도 참조를 추가한다.

추가한다고해서 테이블에 영향은 전혀 주지 않는다.

 

 

Team에 추가

@OneToMany(mappedBy = "team") // Member의 team 과 매핑이 되어 있다.
private List<Member> members = new ArrayList<>();

 

일대다

일대다 단방향

  • 일이 연관관계의 주인
  • 반대편 테이블의 외래 키를 관리하는 특이한 구조
  • JoinColumn을 꼭 사용해야 한다. 그렇지 않으면 조인 테이블 방식을 사용한다. (자동으로 중간 테이블이 추가가됨)

테이블에서는 무조건 다쪽에 외래키가 들어가야되는데, 객체에서는 일에 외래키를 가지고 싶을 수도 있다.

Team의 members가 주인이 되고, 업데이트 될때 member 테이블의 team_id(외래키)가 update되어야 한다.

 

 

Team

@OneToMany
@JoinColumn(name="TEAM_ID") // 연관관계 주인으로 
private List<Member> members = new ArrayList<>();

 

Member는 Team에 대한 아무런 참조를 갖지 않게 한다.

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

Team team = new Team();
team.setName("teamA");
team.getMembers().add(member);

em.persist(team);

tx.commit();

 

이 경우 member와 team insert문이 각각 나가고, Member의 team_id가 update되는 문이 따로 또 나간다.

  • 실무에서 잘 쓰지 않는다.
  • 성능도 느리고, team에 대해 작업했는데 member에 update가 가는게 혼란스러울 수 있다.
  • 엔티티가 관리하는 외래키가 다른 테이블에 있다는 단점
  • 연관관계 관리를 위해 추가로 update SQL 실행

다대일 양방향 매핑을 사용하자

 

 

일대다 양방향

Member

@ManyToOne
@JoinColumn(name="TEAM_ID", insertable = false, updatable = false) // 읽기만
private Team team;

 

Team

@OneToMany
@JoinColumn // 여기서 관리
private List<Member> members = new ArrayList<>();

 

이런 매핑은 공식적으로 존재하지는 않는다.

Team의 members가 연관관계 주인 행사를 하고,

member의 Team도 연관관계 주인처럼 했는데 이건 읽기 전용으로만 만들어버리는 방법이다.

사실상 양방향 매핑한 것과 똑같이 된다.

그냥 다대일 양방향을 사용하도록 하자.

 


일대일

 

주 테이블이나 대상 테이블 중에 외래키 선택 가능

Member에 LOCKER_ID 외래키를 넣어도 되고, Locker에 MEMBER_ID 외래키를 넣어도 된다.

 

 

 

 

@Entity
public class Locker {
    @Id @GeneratedValue
    private Long id;

    private String name;

    @OneToOne(mappedBy = "locker")
    private Member member; // 양방향으로 만들고 싶다면 (읽기 전용)
}
@Entity
public class Member {
		...
    @OneToOne
    @JoinColumn(name="LOCKER_ID")
    private Locker locker; // 연관관계의 주인

 

다대일 양방향 매핑처럼 외래 키가 있는 곳이 연관관계의 주인이 된다.

대상 테이블에 외래키가 있는 단방향 관계는 JPA에서 지원하지 않는다.

(다른 테이블에서 외래키를 관리 하는 것은 안됨)

대상 테이블에 외래 키가 있는 양방향 관계는 가능하다.

Locker에 있는 Member를 연관관계 주인으로 해서 매핑하면 된다.

일대일은 내 엔티티에 있는 외래키만 관리할 수 있다.

 

 

일대일 정리

주 테이블(주로 많이 액세스 하는 테이블)에 외래 키

  • 주 테이블에 외래 키를 두고 대상 테이블을 찾음
  • 장점: 객체 지향 개발자들이 선호, JPA 매핑이 편리, 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능
  • 단점: 값이 없으면 외래 키에 null 허용

대상 테이블에 외래 키

  • 전통적인 데이터베이스 개발자 선호
  • 장점: 주 테이블과 대상 테이블을 일대다 관계로 변경할 때 테이블 구조 유지
  • 단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩

 


 

다대다

 

실무에서는 쓰면 안된다. 

관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없기 때문에

연결 테이블을 추가해 일대다, 다대일 관계로 풀어야 한다. 

 

 

객체는 컬렉션을 사용해 객체 두개로 다대다 관계가 가능하다.

 

@ManyToMany 사용

@JoinTable 로 중간 테이블 지정

다대다 매핑 : 단방향, 양방향 가능

 

 

단방향 관계

 

Member

@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT")
private List<Product> products = new ArrayList<>();

 

Product

@Entity
public class Product {

    @Id @GeneratedValue
    private Long id;

    private String name;
..

중간 테이블, 외래 키 제약 조건 생성

 

 

양방향 관계

Product에 추가

@ManyToMany(mappedBy = "products")
private List<Member> members = new ArrayList<>();

 

편리해 보이지만 실무에서 사용하면 안된다.

중간 테이블에 추가 정보를 더 넣는게 불가능 하다. 매핑 정보만 들어간다.

중간 연결 테이블을 엔티티로 만들어 이 한계를 해결해야 한다.

 

 

중간 엔티티 추가

 

MemberProduct

@Entity
public class MemberProduct {
    @Id @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name="MEMBER_ID")
    private Member member;

    @ManyToOne
    @JoinColumn(name="PRODUCT_ID")
    private Product product;

    private int count;
    private int price;
    private LocalDateTime orderDateTime;
}

 

Member

@OneToMany(mappedBy = "member")
private List<MemberProduct> products = new ArrayList<>();

 

 

Product

@OneToMany(mappedBy = "product")
private List<MemberProduct> memberProducts = new ArrayList<>();

 

 

 

 

 

 

인프런 자바 ORM 표준 JPA 프로그래밍 - 기본편을 수강하고 정리한 글입니다.

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