관계형 데이터베이스에서 관계를 사용하면 중복 데이터를 방지할 수 있음
한 테이블의 레코드가 다른 테이블의 레코드와 일대일로 매핑 되는 경우
즉, 하나의 테이블과 상대 테이블이 반드시 단 하나의 관계를 가지는 것
ex) 사용자 테이블과 프로필 테이블
한 테이블의 레코드가 다른 테이블의 레코드들과 일대다로 매핑되는 경우
일반적으로 외래키(Foreign Key)를 사용하여 관계 설정
ex) 주문 테이블과 주문 상품 테이블
두 개 이상의 테이블이 서로 다대다 관계를 가질 때 사용
보통 중간 테이블을 통해 매핑됨
ex) 학생 테이블과 과목 테이블이 다대다 관계를 가질 때 수강신청 테이블을 사용하여 매핑
DB 테이블에서는 테이블 사이의 연관 관계를 FK(외래키)로 맺을 수 있고 방향 상관없이 조회 가능
Entity에서는 상대 Entity를 참조하여 Entity 사이의 연관 관계를 맺을 수 있음
상대 Entity를 참조하지 않고 있다면 상대 Entity를 조회할 수 있는 방법 X
Entity에서는 DB 테이블에는 없는 방향의 개념이 존재
단방향과 양방향
1) 단방향
한쪽 테이블에서 다른 한쪽의 테이블을 참조할 수 있을 때
2) 양방향
서로 다른 테이블을 조회할 수 있을 떼 즉, 참조할 수 있을 때
@OneToOne Annotation
외래 키의 주인 정하기
()
외래 키의 주인이 활용하는 Annotation
// 음식(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()
을 활용하여 외래 키의 속성 설정
mappedBy
옵션 사용()
으로 설정되고 있는 필드명을 넣어줌// 음식(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를 파악할 수 없어 의도하지 않은 중간 테이블이 생성되기 때문에 반드시 설정!!!!
@ManyToOne Annotation
// 음식(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<>();
}
@OneToMany Annotation
외래 키를 관리하고 있는 주인은 음식 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가 발생된다는 단점 존재
@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;
}
@JoinColumn
의 insertable 과 updatable 옵션을 false로 설정하여 양쪽으로 JOIN 설정을 하면 양뱡향처럼 설정할 수는 있음@ManyToMany Annotation
// 음식(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<>();
}
@ManyToMany
로 음식 Entity를 연결하고 mappedBy 옵션을 설정하여 외래 키의 주인을 설정하면 양방향 관계 맺음이 가능// 음식
@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;