코딩딩/Spring

연관관계 매핑 기초

전낙타 2023. 11. 3. 21:41

 

mappedBy

테이블은 기본으로 FK - PK간의 양방향 연관관계 1개로 이루어져있지만

객체는 FK → PK, PK → FK의 단방향 연관관계 2개로 이루어져 있다

이렇듯 객체끼리의 연관관계를 설정해주려면 각 객체마다 서로를 참조하는 필드값이 존재해야 한다

연관관계의 주인

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

FK를 가지고 있는 객체(Many)가 주인이고 PK로 FK와 다대 일의 관계를 유지하고 있는 객체(One)는 해당 객체를 가지고 있는 List와 mappedBy를 선언해서 주인을 등록한다.

Sub

package hellojpa.section5;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Member5 {

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

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

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

    public Member5(String username, Team team) {
        this.username = username;
        this.team = team;
    }
}

Owner

package hellojpa.section5;

import jakarta.persistence.*;
import lombok.*;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Team {

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

    @ToString.Exclude
    // mappedBy의 존재 의의
    @OneToMany(mappedBy = "team")
    List<Member5> member5List = new ArrayList<>();

}

값을 등록할때는 주인에만 값을 넣어줘도 자동으로 연관관계에 값이 들어간다.

package hellojpa.section5;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;

public class JpaRelationTest2 {
    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try{

            Team team = new Team();
            team.setName("TeamA");
//            team.getMembers().add(member);
            em.persist(team);

            Member5 member = new Member5();
            member.setUsername("member1");
//            연관관계의 주인에만 값을 넣어줘도 다른 연관관계의 값이 자동으로 매핑된다.
            member.setTeam(team);
            em.persist(member);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
    }
}

하지만 순수한 객체 관계를 고려하면 항상 양쪽 다 값을 입력해야 한다

이를 위해 연관관계 편의 메소드를 생성하곤 한다.

public class Member5 {

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

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

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

    public Member5(String username, Team team) {
        this.username = username;
        this.team = team;
    }

		// 연관관계 편의 메소드
		// 반복되는 배열의 경우 람다 스트림 식으로 표현 가능
    public void changeTeam(Team team) {
        this.team = team;
        team.getMembers().add(this);
    }
}