Entity 연관 관계

김나영·2023년 7월 4일
0

Spring

목록 보기
29/38

1. DB table 간의 연관 관계

  • 관계형 데이터베이스에서 관계를 사용하면 중복 데이터를 방지할 수 있음

    • 논리적으로 연관되어 있는 두 테이블 사이의 연결

1) 1대 1(One-to-One Relationship) 관계

  • 한 테이블의 레코드가 다른 테이블의 레코드와 일대일로 매핑 되는 경우

  • 즉, 하나의 테이블과 상대 테이블이 반드시 단 하나의 관계를 가지는 것

  • ex) 사용자 테이블과 프로필 테이블

2) 1대 N(One-to-Many Relationship) 관계

  • 한 테이블의 레코드가 다른 테이블의 레코드들과 일대다로 매핑되는 경우

  • 일반적으로 외래키(Foreign Key)를 사용하여 관계 설정

  • ex) 주문 테이블과 주문 상품 테이블

3) N : M(Many-to-Many Relationship) 관계

  • 두 개 이상의 테이블이 서로 다대다 관계를 가질 때 사용

  • 보통 중간 테이블을 통해 매핑됨

  • ex) 학생 테이블과 과목 테이블이 다대다 관계를 가질 때 수강신청 테이블을 사용하여 매핑

2. Entity간의 연관 관계

  • DB 테이블에서는 테이블 사이의 연관 관계를 FK(외래키)로 맺을 수 있고 방향 상관없이 조회 가능

  • Entity에서는 상대 Entity를 참조하여 Entity 사이의 연관 관계를 맺을 수 있음

  • 상대 Entity를 참조하지 않고 있다면 상대 Entity를 조회할 수 있는 방법 X

  • Entity에서는 DB 테이블에는 없는 방향의 개념이 존재

  • 단방향과 양방향

    1) 단방향

    • 한쪽 테이블에서 다른 한쪽의 테이블을 참조할 수 있을 때

      • 한쪽이라도 상대방의 Entity를 참조하지 못함

    2) 양방향

    • 서로 다른 테이블을 조회할 수 있을 떼 즉, 참조할 수 있을 때

      • 서로 상대방의 Entity를 참조

3. 1대 1 관계

  • @OneToOne Annotation

    • 1대 1 관계를 맺어주는 역할

- 단방향 관계

  • 외래 키의 주인 정하기

    • Entity에서 외래 키의 주인은 일반적으로 N(다)의 관계인 Entity
    • 1대 1 관계에서는 외래 키의 주인을 직접 지정해야함
    • 외래 키 주인만이 외래 키를 등록, 수정, 삭제 할 수 있으며, 주인이 아닌 쪽은 오직 외래 키를 읽기만 가능

- @JoinColumn()

  • 외래 키의 주인이 활용하는 Annotation

    • 컬럼명, null 여부, unique 여부 등을 지정할 수 있음

// 음식(FK 주인) ==> 테이블에 실제 존재하는 FK의 위치
@Entity
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    @OneToOne
    @JoinColumn(name = "user_id")
    private User user;
}
// 고객
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

- 양방향 관계

  • 외래 키의 주인을 지정해 줄 때 mappedBy(외래 키의 주인인 상대 Entity의 필드명) 옵션 사용

  • 설정 방법

    1) 단방향이라면 외래 키의 주인만 상대 Entity 타입의 필드를 가지면서 @JoinColumn() 을 활용하여 외래 키의 속성 설정

    2) 양방향이라면 외래 키의 주인은 상대 Entity 타입의 필드를 가지면서 @JoinColumn()을 활용하여 외래 키의 속성 설정

    • 상대 Entity는 외래 키의 주인 Entity 타입의 필드를 가지면서 mappedBy 옵션 사용
    • 속성 값으로 외래 키의 주인 Entity에 선언된 @JoinColumn() 으로 설정되고 있는 필드명을 넣어줌

// 음식(FK 주인)
@Entity
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    @OneToOne
    @JoinColumn(name = "user_id")
    private User user;
}
// 고객
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToOne(mappedBy = "user")
    private Food food;
}

⚠️ @JoinColum()생략 가능!! 하지만 넣어주는 게 좋다

  • 1대 N 관계에서 외래 키의 주인 Entity가 @JoinColum()을 생략한다면 JPA가 외래 키를 저장할 컬럼을 파악할 수 없어서 의도하지 않은 중간 테이블이 생성되기 때문에
    ⚠️ 양방향 관계에서 mappedBy 옵션을 생략할 경우
  • JPA가 외래 키의 주인 Entity를 파악할 수 없어 의도하지 않은 중간 테이블이 생성되기 때문에 반드시 설정!!!!

4. N대 1 관계

  • @ManyToOne Annotation

    • N대 1 관계를 맺어주는 역할

- 단방향 관계

// 음식(FK 주인)
@Entity
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
}
// 고객
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

- 양방향 관계

// 음식(FK 주인)
@Entity
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user; // 객체
}
// 고객
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToMany(mappedBy = "user")
    private List<Food> foodList = new ArrayList<>();
}

5. 1대 N 관계

  • @OneToMany Annotation

    • 1대 N 관계를 맺어주는 역할

- 단방향 관계

  • 외래 키를 관리하고 있는 주인은 음식 Entity지만 실제 외래 키는 고객 Entity가 가지고 있음

  • N 관계의 테이블이 FK(외래 키)를 가질 수 있기 때문에 FK(외래 키)는 N 관계인 users 테이블에 FK(외래 키) 컬럼을 만들어 추가히지만 FK(외래 키)의 주인인 음식 Entity를 통해 관리함

    //  음식
    @Entity
    @Table(name = "food")
    public class Food {
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
       private String name;
       private double price;
       @OneToMany
       @JoinColumn(name = "food_id") // users 테이블에 food_id 컬럼
       private List<User> userList = new ArrayList<>();
    }
    // 고객
    @Entity
    @Table(name = "users")
    public class User {
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
       private String name;
    }
  • FK를 음식 Entity가 직접 가질 수 있다면 INSERT 발생 시 한 번에 처리할 수 있음

  • 실제 DB에서 FK를 고객 테이블이 가지고 있기 때문에 추가적인 UPDATE가 발생된다는 단점 존재

- 양방향 존재

  • 존재 X
  • 양방향 관계를 맺으려면 음식 Entity를 FK의 주인으로 정해주기 위해 고객 Entity에서 mappedBy 옵션을 사용해야 하지만 @ManyToOne Annotation은 mappedBy 속성 제공 X
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
		@ManyToOne
		@JoinColumn(name = "food_id", insertable = false, updatable = false)
		private Food food;
}
  • N 관계의 Entity인 고객 Entity에서 @JoinColumn의 insertable 과 updatable 옵션을 false로 설정하여 양쪽으로 JOIN 설정을 하면 양뱡향처럼 설정할 수는 있음

6. N대 M 관계

  • @ManyToMany Annotation

    • N대 M 관계를 맺어주는 역할

- 단방향 관계

  • N : M 관계를 풀어내기 위한 중간 테이블(orders) 생성
// 음식(FK 주인)
@Entity
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    @ManyToMany
    @JoinTable(name = "orders", // 중간 테이블 생성
    joinColumns = @JoinColumn(name = "food_id"), // 현재 위치인 Food Entity 에서 중간 테이블로 조인할 컬럼 설정
    inverseJoinColumns = @JoinColumn(name = "user_id")) // 반대 위치인 User Entity 에서 중간 테이블로 조인할 컬럼 설정
    private List<User> userList = new ArrayList<>();
}
// 고객
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}
  • 생성되는 중간 테이블을 컨트롤하기 어렵기 때문에 추후 중간 테이블 변경 시 문제 발생 가능성 존재

- 양방향 관계

// 음식(FK 주인)
@Entity
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    @ManyToMany
    @JoinTable(name = "orders", // 중간 테이블 생성
    joinColumns = @JoinColumn(name = "food_id"), // 현재 위치인 Food Entity 에서 중간 테이블로 조인할 컬럼 설정
    inverseJoinColumns = @JoinColumn(name = "user_id")) // 반대 위치인 User Entity 에서 중간 테이블로 조인할 컬럼 설정
    private List<User> userList = new ArrayList<>();
}
// 고객
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @ManyToMany(mappedBy = "userList")
    private List<Food> foodList = new ArrayList<>();
}
  • 반대 방향인 고객 Entity에 @ManyToMany로 음식 Entity를 연결하고 mappedBy 옵션을 설정하여 외래 키의 주인을 설정하면 양방향 관계 맺음이 가능

- 중간 테이블

  • 중간 테이블 orders를 직접 생성하여 관리하면 변경 발생 시 컨트롤 쉬움 ==> 확장성에 좋음
// 음식
@Entity
@Getter
@Setter
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    @OneToMany(mappedBy = "food") // N : 주문은 여러 번 될 수 있기 때문에
    private List<Order> orderList = new ArrayList<>();
    // Order 테이블을 통해서 User를 줘야 할 생각이 없다면 생략 가능
}
// 고객
@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToMany(mappedBy = "user") // 고객은 One, 주문을 여러 번 할 수 있음
    private List<Order> orderList = new ArrayList<>();
    // Order 테이블을 통해서 Food를 줘야 할 생각이 없다면 생략 가능
}
// 주문
@Entity
@Getter
@Setter
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 관계를 맺기 위해 직접 만들었기 때문에 각각 생성해줘야함
    @ManyToOne // Many : 처음에 오는게 현재 Entity 클래스
              // One : 두 번째 오는게 상대 Entity
    @JoinColumn(name = "food_id")
    private Food food;
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

0개의 댓글