본문 바로가기
백엔드

[Spring Boot] JPA 연관관계 매핑

by 육빔 2024. 9. 1.
728x90
반응형

우리가 보통 프로젝트에서 진행하는 엔티티들은 다른 엔티티와 관계가 있다. 하지만 객체는 참조를 사용하고 데이터베이스는 fk로 관계를 맺는다. 이런 다른 특징을 객체에서 매핑 시켜줄려면 단방향 관계 2개로 조인을 해주어야한다.

이때 사용하는 매핑은 @JoinColomn을 사용한다.

 

이러한 연관관계를 매핑하기 위해선 3가지를 고려해야한다.

(1) 다중성

(2) 단방향, 양방향

(3) 연관관계의 주인 

 

다중성은 아래와 같은 다중성이 있다.

 

-다대일(@ManyToOne)

-일대다(@OneToMany)

-일대일(@OneToOne)

-다대다(@ManyToMany)

여기서 거의 대부분 사용하는 것은 @ManyToOne이고 일대일과 다대다는 거의 사용하지 않는다.

 

사실 양방향으로 객체끼리 조회하기 위해선 @ManyToOne을 적용한 반대편엔 @OneToMany를, @OneToMany를 적용한 반대편엔 @OneToMany를 적용해야 한다. 이렇게 서로 적용한 것을 양방향 관계라고 한다.

 

이렇게 양방향 매핑을 할때는 연관관계의 주인을 지정해줘야하는데 여기서 주인은 외래키를 변경가능한 곳, 수정가는한 곳을 지칭한다. 주인이 아닌 반대편에는 mappedBy를 사용하여 주인 필드의 이름을 값으로 입력해야한다.

 

 

다대일(@ManyToOne)

 

거의 대부분의 관계에서 사용하는 관계.

 

(1) 다대일 단방향

@Entity
public class Member {
    
    @Id @GeneratedValue
    private Long id;
    
    private String name;
    
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
    
}

@Entity
public class Team {
    
    @Id @GeneratedValue
    private Long id;
    
    private String name;
    
}

 

이런 경우 보통 데이터베이스에서는 Member에 fk가 들어가서 Team을 참조하게 된다. 하지만 객체에서 구현하기 위해선 Member.java를 살펴보면 @JoinColumn을 통하여 Team을 연결하는걸 볼 수 있다. 하지만 Team.java에는 아무런 코드가 없는데 아무런 참조를 안하고 있기 때문이다. 여기서 Team을 통해 접근하기 위해선 양방향 매핑을 진행해주어야한다.

 

(2) 다대일 양방향

@Entity
public class Member {
    
    @Id @GeneratedValue
    private Long id;
    
    private String name;
    
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
    
}

@Entity
public class Team {
    
    @Id @GeneratedValue
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
    
}

 

여기서 mappedBy = "반대편 Team 객체의 변수명" 으로 진행한다. 또한 @OneToMany 어노테이션 또한 작성해줘야한다. 외래키가 있는 쪽이 연관관계의 주인이  주인이 되고 양쪽이 서로 참조.

 

일대다(@OneToMany)

 

일대다 관계에서는 1이 연관관계의 주인이 됩니다. 즉 위 같은 Team에서 외래키를 관리한다는 의미입니다.

 

일대다 관계는 엔티티를 1:N이므로 자바의 List, Set, Map 등을 사용하여 여러개의 정보를 저장할 수 있게 해야됩니다.

(1) 일대다 단방향

@Entity
public class Member {
    
    @Id @GeneratedValue
    private Long id;
    
    private String name;
}

@Entity
public class Team {
    
    @Id @GeneratedValue
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
    
}

 

(1) 일대다 양방향

반대편의 경우도 전과 동일하게 ManyToOne으로 매핑해준다. 코드 생략

 

일대다 관계의 단점은 연관관계를 처리를 위해 UPDATE 쿼리를 추가로 실행한다. 그러므로 잘 사용하지 않는다. 

 

일대일(@OneToOne)

@Entity
public class User {
    
    @Id @GeneratedValue
    private Long id;
    
    private String username;
    
    @OneToOne
    @JoinColumn(name = "locker_id")
    private Locker locker;
}

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

 

보통 외래 키를 어느 쪽에 두어도 관계 없지만, 일반적으로 액세스 빈도가 높은 쪽에 외래 키를 둡니다. 서로 OneToOne을 사용해주기만 하면 오케이다. 여기서 관점이 나뉘는데

전통적인 데이터베이스 개발자들은 대상 테이블에 외래 키를 두는 것을 선호한다.

이 방법의 장점은 일대일에서 일대다로 확장할때 테이블 구조를 그대로 사용할 수 있다.

 

객체지향 개발자들은 주 테이블에 외래 키가 있는 것을 더욱 선호한다. 위 같은 케이스가 주 테이블에 외래 키인 경우다.

 

대상 테이블에 외래 키의 단방향 관계는 JPA에서 지원하지 않기에 양방향 매핑을 진행 해 준뒤 mappedBy로 주인을 설정해주자.

 

 

다대다(@ManyToMany)

@Entity
public class Student {
    
    @Id @GeneratedValue
    private Long id;
    
    private String name;
    
    @ManyToMany
    @JoinTable(name = "student_course",
               joinColumns = @JoinColumn(name = "student_id"),
               inverseJoinColumns = @JoinColumn(name = "course_id"))
    private List<Course> courses = new ArrayList<>();
    
}

@Entity
public class Course {
    
    @Id @GeneratedValue
    private Long id;
    
    private String title;
    
    @ManyToMany(mappedBy = "courses")
    private List<Student> students = new ArrayList<>();
    
}

 

다대다 관계는 두 엔티티 간에 서로 여러 개씩의 엔티티가 연결될 때 사용됩니다. 일반적으로 @ManyToMany는 사용하지 않고, 중간에 연결 테이블을 나타내는 엔티티를 만들어 다대일, 일대다로 풀어서 매핑합니다. 이렇게 하면 매핑할 엔티티를 하나 덜 만들어도 된다는 장점이 있으나, 중간 테이블은 Member와 Product의 N : N 관계를 (1 : N) + (N : 1) 관계로 풀어 연결해줍니다. 또한 중간테이블이 숨겨져 있기 때문에 예상하지 못한 쿼리가 날라갑니다. 이러한 이유로 인해 실무에서는 거의 사용하지 않는 방법입니다.

 

 

728x90
반응형