2019/10/27 - [프로그래밍 노트/JPA] - [JPA] 다양한 연관관계_일대다(1:N)
일대다(1:N) 단뱡향 관계일 경우, 일대다 단방향관계보단 다대일 양방향관계를 사용해야한다는 포스팅을 한적이있다.
이에관련한 내용을 더 알아보자.
member 테이블과 member_detail 테이블은 N:1 관계이다.
다대일(N:1) 단방향관계일때 엔티티를 저장해보자
MemberDetail : Member = N : 1
Member.java
@Getter
@Setter
@Entity
public class Member {
@Id
private Long memberId;
private String name;
private LocalDateTime createDate;
}
MemberDetail.java
@Getter
@Setter
@Entity
public class MemberDetail {
@Id
private Long memberDetailId;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "member_id")
private Member member;
private String type;
private String description;
}
MemberDetail에서 Member로 @MnayToOne 관계를 설정했다.
MemberService.java
멤버와 멤버디테일을 저장하는 서비스이다.
@Transactional
public void createMemberWithDetails(){
Member member = new Member("member1", LocalDateTime.now());
MemberDetail memberDetail1 = new MemberDetail();
memberDetail1.setMember(member);
memberDetail1.setType("type1");
memberDetail1.setDescription("member1-type1");
MemberDetail memberDetail2 = new MemberDetail();
memberDetail2.setMember(member);
memberDetail2.setType("type2");
memberDetail2.setDescription("member1-type2");
memberDetailRepository.saveAll(Arrays.asList(memberDetail1, memberDetail2));
}
우리가 원하는대로 저장이 깔끔하게 되는 것을 볼 수 있다.
Hibernate: insert into member (create_date, name, member_id) values (?, ?, ?)
Hibernate: insert into member_detail (description, member_id, type, member_detail_id) values (?, ?, ?, ?)
Hibernate: insert into member_detail (description, member_id, type, member_detail_id) values (?, ?, ?, ?)
일대다(1:N) 단방향관계일때 엔티티를 저장해보자
Member : MemberDetail = 1 : N
Member.java
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long memberId;
private String name;
private LocalDateTime createDate;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name="memberId")
private List<MemberDetail> details;
public Member(String name, LocalDateTime createDate) {
this.name = name;
this.createDate = createDate;
}
}
Member에서 MemberDetail로 @OneToMany 관계를 설정했다.
MemberDetail.java
@Entity
public class MemberDetail {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long memberDetailId;
private Long memberId;
private String type;
private String description;
}
MemberService.java
@Transactional
public void createMemberWithDetails(){
Member member = new Member("member1", LocalDateTime.now());
member = memberRepository.save(member);
MemberDetail memberDetail1 = new MemberDetail();
memberDetail1.setMemberId(member.getMemberId());
memberDetail1.setType("type1");
memberDetail1.setDescription("member1-type1");
MemberDetail memberDetail2 = new MemberDetail();
memberDetail1.setMemberId(member.getMemberId());
memberDetail2.setType("type2");
memberDetail2.setDescription("member1-type2");
member.setDetails(Arrays.asList(memberDetail1, memberDetail2));
}
위의 결과처럼 짠! 하고 insert문이 3개가 나갈 것 같지만, 결과는..
Hibernate: insert into member (create_date, name, member_id) values (?, ?, ?)
Hibernate: insert into member_detail (description, member_id, type, member_detail_id) values (?, ?, ?, ?)
Hibernate: insert into member_detail (description, member_id, type, member_detail_id) values (?, ?, ?, ?)
Hibernate: update member_detail set member_id=? where member_detail_id=?
Hibernate: update member_detail set member_id=? where member_detail_id=?
update문이 2개 더 나가는 것을 알 수 있다. 이전 포스팅에서도 말했지만, 본인 테이블에 외래키가 있으면 엔티티의 저장과 연관관계 처리를 INSERT SQL 한번으로 처리할 수 있지만, 다른 테이블의 외래키가 있을 경우에는 UPDATE SQL이 추가로 실행된다.
여기서 본인 테이블은 Member가 되고, 외래키는 member_id가 된다. 외래키는 MemberDetail에 존재하므로 위와같은 현상이 발생한 것이다.
그렇다면 어떻게 해야할까..?
Member클래스에서 객체그래프 탐색으로 MemberDetail을 사용해야하는 상황이다.
그렇다. 사람들이 말하는 일대다 단방향보다는 다대일 양방향 매핑을 사용해야 한다.
다대일 양방향 매핑 사용하기
Member.java
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long memberId;
private String name;
private LocalDateTime createDate;
@OneToMany(mappedBy = "member")
private List<MemberDetail> details;
public Member(String name, LocalDateTime createDate) {
this.name = name;
this.createDate = createDate;
}
}
MemberDetail.java
@Entity
public class MemberDetail {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long memberDetailId;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="memberId")
private Member member;
private String type;
private String description;
}
MemberDetail에 @ManyToOne 연관관계를 추가하여, 양방향 연관관계를 맺었다.
MemberService.java
@Transactional
public void createMemberWithDetails(){
Member member = new Member("member1", LocalDateTime.now());
//member = memberRepository.save(member);
MemberDetail memberDetail1 = new MemberDetail();
memberDetail1.setMember(member);
memberDetail1.setType("type1");
memberDetail1.setDescription("member1-type1");
MemberDetail memberDetail2 = new MemberDetail();
memberDetail1.setMember(member);
memberDetail2.setType("type2");
memberDetail2.setDescription("member1-type2");
member.setDetails(Arrays.asList(memberDetail1, memberDetail2));
memberDetailRepository.saveAll(Arrays.asList(memberDetail1, memberDetail2));
}
양방향 매핑을 사용하면 깔끔하게 INSERT문이 3번나가는 것을 볼 수 있다.
'프로그래밍 노트 > JPA' 카테고리의 다른 글
[JPA] N+1 문제 해결방법 (0) | 2020.12.21 |
---|---|
[QueryDSL] QueryDSL로 SQL Replace 사용하기 (0) | 2020.11.06 |
[JPA] 다양한 연관관계_일대다(1:N) (2) | 2019.10.27 |
[JPA] 다양한 연관관계_다대일(N:1) (0) | 2019.10.27 |
[JPA] 연관관계 매핑 기초(단방향/양방향) (5) | 2019.10.10 |