연관관계 매핑 기초

OneTwoThree·2023년 7월 25일
0

출처


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

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

            tx.commit();

연관관계 매핑을 배우지 않으면 이렇게 객체지향스럽지 않은 코드를 짜야 한다
테이블은 외래 키로 조인을 사용해 연관된 테이블을 찾는다.
객체는 참조를 사용해서 연관된 객체를 찾는다.

단방향 연관관계

@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name="MEMBER_ID")
    private Long id;

    @Column(name="USERNAME")
    private String username;

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

@ManyToOne : Member가 many, Team이 one
@JoinColumn : 조인할 때 쓰는 컬럼명

이렇게 연관관계 매핑을 하면 Team을 바로 set하고 수정할 수 있다

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

테이블의 연관관계는 FK 하나로 양방향 연관관계가 다 있다. (JOIN 하면 되기 때문)
반면에 객체에서는 Team에서 List members 를 필드로 가져야 Member를 참조할 수 있다.

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

일대다 연관관계이고 Member 클래스의 team 필드랑 mapped 되어있다.
이렇게 Member에서 Team을 참조할 수 있고 Team에서 Member를 참조할 수 있다
객체는 가급적이면 단방향이 좋다

  • 객체와 테이블이 관계를 맺는 차이
    객체 : 연관관계 2개
    회원 -> 팀 연관관계 1개 (단방향)
    팀 -> 회원 연관관계 1개 (단방향)
    테이블 : 연관관계 1개
    회원<->팀 연관관계 1개 (양방향)

객체의 양방향 관계는 사실 양방향 관계가 아니라 단방향 관계 2개이다

이런 상황에서 팀을 바꾸고 싶으면 Member의 team을 바꿔야 할지 Team의 mebers를 바꿔야 할지 애매하다

  • 객체의 두 관계 중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인 만이 외래 키를 관리(등록, 수정)
  • 주인이 아닌 쪽은 읽기만 가능
  • 주인은 mappedBy 속성 사용 X
  • 주인이 아니면 mappedBy 속성으로 주인 지정

FK가 있는 곳을 주인으로 정해라

FK가 있는 Member.team이 연관관계의 주인이다.
1대다일경우 인 쪽이 연관관계의 주인이 되어야 한다

주의점

  • member.setTeam(team)
  • 연관관계의 주인에 값을 입력해 줘야 한다.
  • 양방향 매핑인 경우 양쪽에 다 값을 넣어주는게 좋다
  • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
  • 연관관계 편의 메소드를 정의할 수 있다
  • 컨트롤러에서는 절대 엔티티를 반환하지 마라, 무한루프 발생 가능
    • 웬만하면 dto로 변환해서 반환하는 것이 좋다
  • 단방향 매핑만으로 이미 연관관계 매핑은 완료
  • 양방향 매핑은 반대 방향 조회 기능이 추가된 것 뿐
  • 양방향은 필요할 때 추가해도 됨
  • 연관관계의 주인을 정하는 기준은 비즈니스 로직이 아니라 외래 키의 위치를 기준으로 정해야 함

예제

@Entity
public class OrderItem {

    @Id @GeneratedValue
    @Column(name="ORDER_ITEM_ID")
    private Long id;

    @ManyToOne
    @JoinColumn(name="ORDER_ID")
    private Order order;

    @ManyToOne
    @JoinColumn(name="ITEM_ID")
    private Item item;

@ManyToOne@JoinColumn을 사용해서 연관관계를 매핑한다.

@Entity
@Table(name = "ORDERS")
public class Order {

    @Id
    @GeneratedValue
    @Column(name="ORDER_ID")
    private Long id;
    
    @ManyToOne
    @JoinColumn(name="MEMBER_ID")
    private Member member;

마찬가지로 @ManyToOne , @JoinColumn을 사용한다.

외래 키가 있는 테이블 쪽에서만 단방향으로 연관관계 매핑을 한다.

실무에서는 jpql을 쓰거나 조회를 편리하게 하기 위해 양방향 연관관계를 추가하는 경우가 있다.

@Entity
@Table(name = "ORDERS")
public class Order {

    @Id
    @GeneratedValue
    @Column(name="ORDER_ID")
    private Long id;

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

    @OneToMany(mappedBy = "order")
    private List<OrderItem> orderItems;
    
    private LocalDateTime orderDate;
    
    @Enumerated(EnumType.STRING)
    private OrderStatus status;

양방향 연관관계를 추가한다면 다음과 같이 mappedBy를 사용한다.

0개의 댓글