한동안 나태해져있는 상태여서 스프링 공부를 미루고 백준만 찔끔찔끔 풀면서 지내왔다.
오늘만큼은 무조건 스프링 공부를 함으로써 감각을 유지해야겠다 싶어서
스타벅스에 와서 JPA 기본편 강좌를 듣고 포스팅하는 중이다.
인프런 김영한의 자바 ORM 표준 JPA 프로그래밍 기본편 강좌를 듣고
복습 및 요약한 내용입니다.
틀린 점 및 추가할 점은 댓글로 피드백 부탁드립니다 :)
N:1 연관관계 매핑
다대일 연관관계 매핑에 대해 공부해보자.
일대다가 좋을까 다대일이 좋을까?
강의에서는 웬만해선 다대일(N:1) 연관관계 매핑을 사용할 것을 강조하고 있다.
그리고 웬만해선 단방향 매핑으로만 해결하되, 양방향 매핑이 필요할 경우에만 양방향을 사용할 것을 권장하고 있다.
일단 관계형 데이터베이스 제1정규화 규칙으로 무결성을 위해
FK가 Member(다대일의 다)에 있는 것은 당연하다.
다대일 단방향 관계에선, 연관관계의 주인 또한 Member 클래스에 있는 상황이다.
연관관계의 주인도 Member class이고,
테이블 연관관계에서의 주인 또한 Member table이므로
헷갈릴 일이 없다.
주인은 그냥 '다'에 속하는 테이블이라 생각하면 된다.
package hellojpa;
import javax.persistence.*;
@Entity
public class Member1 {
@Id @GeneratedValue
@Column(name="MEMBER_ID")
private Long id;
@Column(name="USERNAME")
private String username;
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
}
Member1 클래스 코드이다. Getter, Setter 첨부는 코드가 너무 길어져서 생략하였다.
PK에 해당하는 id는 @Id, @GeneratedValue 어노테이션으로,
FK에 해당하는 team은 @ManyToOne 어노테이션으로 다대일 연관관계 매핑을 해주었고,
@JoinColumn으로 외래키 연관관계 매핑에 사용됨을 알려주었다.
package hellojpa;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Team {
@Id @GeneratedValue
@Column(name="TEAM_ID")
private Long id;
private String name;
// 양방향 매핑 필요한 경우 추가
// @OneToMany(mappedBy="team")
// private List<Member1> members = new ArrayList<>();
}
Team 클래스 코드이다.
이 클래스는 연관관계 주인이 아니므로 딱히 별다른 코드가 없다.
team에서 멤버 관계를 파악해야 할 때 jpql을 사용해야 한다면 양방향 매핑코드를 추가하면 되겠다.
다대일 연관관계 매핑을 보면서 아래와 같은 의문이 들 수 있다.
???: 단순히 연관관계 주인이 테이블, 객체 모두 같다는 점 때문에 N:1 연관관계 매핑 설계를 하는 것이냐?
1:N 연관관계 매핑도 JPA에서 지원해주는데 왜 이건 거의 안쓰는 것이냐?
당연히 단순히 이러한 점 때문에만 N:1 연관관계 매핑을 해주는 것은 아니다.
밑에 적겠지만 1:N 연관관계 매핑에서의 단점 때문에 N:1 연관관계 매핑을 해주는 것이다.
1:N 연관관계 매핑
그럼 이 매핑은 왜 사용되지 않을까?
다대일 연관관계 매핑과 다르게,
객체의 연관관계 주인이 서로 반대 테이블의 FK를 관리하고 있음을 알 수 있다.
즉, 엔티티가 관리하는 외래 키가 Team 테이블이 아닌, Member 테이블에 있으며
이러한 점 때문에 JPA에 익숙하지 않은 경우라면
테이블이 굉장히 여러개인 경우 (즉, 대부분의 경우) 반대편 테이블에서 업데이트가 되기 때문에 굉장히 헷갈린다고 한다. (난 아직 실무를 해본적이 없으니까 잘 모르겠음ㅋㅋ)
또한, Team 클래스에서 테이블의 외래키를 관리하는데
테이블 연관관계에선 Member 테이블이 FK를 가지고 있으므로
Team 테이블에선 단순 조회만 가능하고 외래키에 영향을 줄 수 없는 상황이므로
team 생성은 team 테이블에 insert해주면 되기 때문에 크게 상관이 없으나, team에 멤버가 새로 추가될 때, team의 members 리스트에 member를 add하는 코드를 작성하는데 단순히 team 객체 클래스의 members가 업데이트된 것이고, em.persist(team)을 할 때, 테이블의 연관관계 주인은 Member이므로 team 테이블을 업데이트해주기 위해 새로운 중간테이블을 JPA가 생성하고 update 쿼리를 추가적으로 날리는 것을 볼 수 있다.
매핑한 객체가 Member 클래스였다면 FK 또한 Member에 있으므로 단순히 team 정보 세팅 후 insert 해주면 되는데,
매핑한 객체가 반대 테이블의 FK를 관리하고 있기 때문에 team에 member 정보 세팅 후, member에 insert가 되고, member엔 team에 대한 정보가 없으므로 (있다면 양방향 매핑인데, 1:N 양방향 매핑은 존재하지 않는다. 그 이유는 밑에 쓰겠다.) team_member 중간테이블이 생성되는 방식으로 update 쿼리가 추가로 날라가는 것이다.
즉, 성능적으로 다대일 연관관계 매핑보다 저하됨을 알 수 있다.
www.inflearn.com/questions/18042
개인적으로 위 두개의 글을 읽으면 1:N 양방향 매핑이 존재하지 않는 이유를 잘 알 수 있을 것이다.
아직 내가 많이 부족해 괜히 어정쩡하게 글 쓸 바에 링크를 첨부해서 나도 공부하고, 이 글을 보는 여러분들도 같이 공부하면 좋을 듯 하여 링크를 첨부한다.
사실 위에 쓴 글 또한 아직 실력이 부족해 잘못됐을 가능성도 존재한다.... 여러분의 피드백이 절실하다.
테이블의 FK는 무조건 '다'에 존재하며,
FK를 관리하는 객체 또한 같은 테이블의 객체일 때
보기에도 좋고 성능도 좋으므로
N:1 연관관계 매핑을 이용하자.
1:N으로 하면 반대 테이블의 FK를 관리하는 매핑이기 때문에
성능적으로도, 외관상으로도 좋지 않다.
+ 나중에 다시 한 번 공부를 해야하는 부분일 듯 하다.
'JAVA > JPA 학습기록' 카테고리의 다른 글
[JPA] 실전예제 5_ 영속성 전이(Cascade), 고아 객체(orphanRemoval=true) 적용 (0) | 2021.05.15 |
---|---|
[JPA] 실전예제 5_지연로딩(FetchType.lazy) 적용 (0) | 2021.05.15 |
[JPA] 실전예제1_요구사항 분석과 기본매핑까지 수강했다 (0) | 2021.04.17 |
[JPA] 영속성 컨테이너, 그리고 회원 CRUD (0) | 2021.04.08 |
[Spring] JPA 기본편 환경세팅하면서 생겼던 시행착오 (0) | 2021.04.03 |