OneToOne - 1:1OneToMany - 1:NManyToOne - N:1ManyToMany - N:M >>> 실무 사용 금지! N:M 연관관계는 RDB에서 일반적인 방법으로 표현할 수 없어서 중간 테이블이 생기게 된다.
따로 중간 엔티티를 만들어서 1:N, N:1 관계로 분해하지 않으면 관리가 힘들어지기 때문에 사용을 권장하지 않는다.
실제 작성한 특강신청 프로젝트의 일부를 발췌했다.
@Entity
@Getter//getter, setter 자동생성
@NoArgsConstructor //기본 생성자 추가
@AllArgsConstructor //모든 필드에 대한 생성자 추가
@Table(name="LECTURE_HISTORY")
@Builder
public class LectureHistory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="history_id")
private Long historyId;
@ManyToOne
@JoinColumn(name="student_id")
private Student student;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="lecture_id")
private Lecture lecture;
@Column(name="reg_date")
private LocalDateTime regDate = LocalDateTime.now();
public LectureHistory(Student student, Lecture lecture) {
this.student = student;
this.lecture = lecture;
}
}
@ManyToOne가 설정된 필드는 모두 List타입을 이용했다.
List 말고도 Set, Map타입도 이용 가능.
List가 아닌 그냥 List로도 선언이 가능하나 이럴 때에는 targetEntity의 명시가 필요하다.
@OneToMany(targetEntity = Student.class)
private List student;
하지만 이런 경우는 권장 x, 많이 사용되지 x
단방향 관게 : 두 엔티티가 관계를 맺을 때, 한 쪽의 엔티티만 참조하고 있는 것.
양방향 관계 : 두 엔티티가 관계를 맺을 때, 양 쪽이 서로 참조하고 있는 것.
@ManyToOne기준으로
"단방향(unidirectional)"은 상대 엔티티에 @OneToMany가 없는 경우,
"양방향(bidirectional)"은 상대 엔티티에 @OneToMany가 있는 경우.
@OneToMany 양방향과 @ManyToOne 양방향은 기준이 다를 뿐, 차이가 없다.
단뱡향이든 양방향이든 @OneToMany 어노테이션을 달고 있는 엔티티가 부모 엔티티가 된다.
즉, FK를 들고 있는 쪽이 자식 엔티티가 되는 것.
가장 많이 쓰이는 연관관계.
엔티티의 관계를 표현하고 FK관리에 있어서 가장 자연스럽다.
@JoinColumn과 함께 쓰이며 엔티티 테이블에 FK칼럼을 정의해준다.
@ManyToOne
@JoinColumn(name="student_id")
private Student student;
상대 엔티티를 참조할 수 있는 매핑이 부모 엔티티 쪽에 존재하지만, FK는 자식 엔티티 테이블에 존재하는 연관관계.
@JoinColumn없이 사용할 경우, Hibernate에서 자체적으로 중간 테이블을 생성해서 연관관계를 관리하게 된다.
=> FK컬럼을 만드는 것 보다 비효율적. 자식엔티티를 제거할 때 심각한 성능문제로 이어질 수 있다.
이를 방지하기 위해
/* Lecture.java */
@OneToMany
@JoinColumn(name = "lecutre_id")
private List<Lecture> lectures;
여기서 사용되는 @JoinColumn은 상대 엔티티 테이블에 FK컬럼이 있음을 알려줌.
name 속성에는 매핑 할 외래 키 이름을 지정함.
DB관점에서는 단방향 @ManyToOne과 차이점 x
어플리케이션에서 상대 엔티티쪽에서 참조할 수 있는 변수가 생기는 장점이 있다.
/* Lecture.java */
@OneToMany(mappedBy = "user") //mappedBy설정하지 않으면 @OneToMany가 단방향 취급되어 중간 테이블 생김!!!
private List<History> history;
/* History.java */
@ManyToOne
@JoinColumn
private History history;
연관관계를 갖는 두 테이블에서 외래키를 갖게되는 테이블이 연관관계의 주인이 됨.
연관관계의 주인만이 외래키를 관리(CUD) 할 수 있고 주인이 아닌 엔티티는 읽기만 할 수 있다.
@OneToMany를 설정한 엔티티는 자식 엔티티에 영향을 줄 수 있다.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="lecture_id")
private Lecture lecture;
연관관계에 있는 엔티티에 접근할 때 DB에 쿼리를 날려 엔티티 조회.
접근하지 않는 경우 쿼리가 발생하지 않음.
상대 엔티티의 조회 여부와 상관없이 쿼리가 발생함.
@OneToMany의 기본값은 Lazy Fetch이며, @ManyToOne의 기본값은 Eager Fetch.
Eager Fetch, Lazy Fetch 상관 없이 단건 조회가 아닌 컬렉션 조회에서 N+1 문제가 발생할 수 있다.
Eager Fetch는 조회 여부와 상관없이 쿼리가 발생하기 때문에, 더 잘 보이는 차이가 있을 뿐이다.
영속성 전파 설정시, 객체에 해당 작업이 이루어질 때 "자식엔티티에도 작업이 전파됨".
PERSIST, MERGE, REMOVE, REFRESH, DETACH와 모든 옵션을 줄 수 있는 ALL
예를들어 Student의 lecturelist에 cascadeType으로 PERSIST가 걸려있으면, Student객체만 저장해도 lecturelist객체에 저장된다.
@ManyToOne에만 존재하는 옵션.
FK칼럼에 Null여부를 설정함.
기본값은 true, false인 경우 FK에 Null을 허용하지 않는다.
양방향 연관관계가 설정된 경우라도 DB에서는 오직 한 쪽 테이블만 FK를 들고있다.
양방향 연관관계가 설정된 경우 연관관계를 일관성있게 유지해야한다.
@ToString()을 주의해야한다.
@ToString()은 해당 클래스의 모든 멤버변수를 출력하는데, 연관관계 매핑이 되어있을 경우 그 객체 역시 출력해야하기 때문에 이 때 데이터베이스 연결이 필요하게 된다.exclude(해당 속성값으로 지정된 필드는 toString()에서 제외해줌)속성을 사용하는 것이 좋다. 출처