Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- Servlet
- Spring Boot
- 스프링
- 그리디
- SpringBoot
- pointcut
- 알고리즘
- Thymeleaf
- JPQL
- AOP
- 인프런
- 자바
- springdatajpa
- transaction
- Proxy
- 김영한
- spring
- JDBC
- Exception
- db
- 스프링 핵심 기능
- QueryDSL
- kotlin
- 스프링 핵심 원리
- 백준
- Greedy
- Android
- http
- jpa
- java
Archives
- Today
- Total
개발자되기 프로젝트
도메인 모델, 연관관계 본문
1. 도메인 모델
- 회원과 게시글의 관계 : 양방향 일대다
- 한 회원은 여러 게시물 작성 가능
- 게시글에서 작상한 회원 참조 필요.
- 회원과 댓글의 관계 :양방향 일대다
- 한 회원은 여러 댓글 작성 가능
- 댓글에서 작성한 회원 참조 필요.
- 게시글과 댓글의 관계 : 양방향 일대다
- 한 게시글에 여러 댓글 가능.
- 댓글 작성된 게시글 참조 필요.
2. Entity 분석
- Comment: 대댓글 기능을 위해 Parent, Chield로 부모자식 comment를 연결.
3. 테이블 분석, 설계
- Comment child 입장에서 "다"측인 parent의 PK를 FK로 가지고 있어야 함.
4. 연관관계 매핑.
4.1 MemberClass
@Entity @Getter
public class Member {
@Id @Column(name = "MEMBER_ID")
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "member")
private List<Post> posts = new ArrayList<>();
@OneToMany(mappedBy = "member")
private List<Comment> comments = new ArrayList<>();
///
}
4.2 Post Class
@Entity @Getter
public class Post {
@Id @GeneratedValue
@Column(name = "POST_ID")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member;
@OneToMany(mappedBy = "post")
private List<Comment> comments = new ArrayList<>();
//연관관계 편의 메서드
public void setMember(Member member){
this.member = member;
member.getPosts().add(this);
}
////
}
- 양방향 매핑을 누락하지 않기 위해 "다"측에 연관관계 편의 메서드 생성.
- FetchType.LAZY : post.getMember 호출 시에 member 불러오도록 lazy 지정.
- EAGER로 지정할 경우 post 호출 시 member 항상 호출됨.
4.3 Comment Class
@Entity
@Getter
public class Comment {
@Id
@GeneratedValue
@Column(name = "COMMENT_ID")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "POST_ID")
private Post post;
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Comment parent;
@OneToMany(mappedBy = "parent")
private List<Comment> child = new ArrayList<>();
//연관관계 편의 메서드
public void setMember(Member member){
this.member = member;
member.getComments().add(this);
}
public void setPost(Post post){
this.post = post;
post.getComments().add(this);
}
public void setParent(Comment parent){
this.parent = parent;
parent.getChild().add(this);
}
///
}
- 양방향 매핑을 누락하지 않기 위해 "다"측에 연관관계 편의 메서드 생성.
- FetchType.LAZY : comment.getMember 호출 시에 member 불러오도록 lazy 지정.
- EAGER로 지정할 경우 comment 호출 시 member 항상 호출됨
- Post도 동일하게 적용.
- Comment의 경우 계층 구조를 가지고 있음.
- Parent와 child는 일대다 관계
- 계층구조 연관관계 설정 시 특이한 점이 있다.
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Comment parent;
- @JoinColumn으로 FK를 지정하는데..parent는 Comment객체이므로 "COMMEN_ID"가 맞을 것 같은데 아니다.
- 오히려 "PARENT_ID"로 등록되어야 가능하다. 만약 "COMMENT_ID"로 적음 에러난다.
- 그 이유는 @JoinColumn의 referencedColumnName()이다.
- referencedColumnName()을 별도로 지정하지 않으면 referencedTable(매핑하는 테이블)의 PK를 자동으로 지정함.
- 즉, "PARENT_ID"는 그냥 FK으로 사용하는 이름이고 실제 값은 Comment parent의 PK인 "COMMENT_ID"이다.
https://bsh-developer.tistory.com/206
5. Test
@Transactional
@SpringBootTest
@Rollback(value = false)
public class DomainTest {
@Autowired MemberService memberService;
@Autowired PostService postService;
@Autowired CommentService commentService;
@Test
public void domainMappingTest(){
//given
Member member = createMember();
Post post = createPost();
Comment comment = createComment();
//when
post.setMember(member);
comment.setPost(post);
comment.setMember(member);
memberService.joinMember(member);
commentService.saveComment(comment);
postService.savePost(post);
//then
assertThat(comment.getMember().getName()).isEqualTo(member.getName());
assertThat(comment.getPost().getTitle()).isEqualTo(post.getTitle());
assertThat(post.getMember().getName()).isEqualTo(member.getName());
}
public Member createMember(){
return new Member("memberA", "abc123", "123465!", "123@navber.com");
}
public Post createPost(){
return new Post("title", "content123");
}
public Comment createComment(){
return new Comment("comment_content");
}
}
- 의도한 대로 테이블이 생성되었다.
- 연관관계도 잘 설정이 된 것을 확인이 가능하다.
6. 결과 log
insert
into
member
(email, name, password, user_id, member_id)
values
(?, ?, ?, ?, ?)
2021-10-03 22:37:31.717 DEBUG 31972 --- [ main] org.hibernate.SQL :
insert
into
comment
(content, member_id, post_id, id)
values
(?, ?, ?, ?)
2021-10-03 22:37:31.720 DEBUG 31972 --- [ main] org.hibernate.SQL :
insert
into
post
(content, member_id, title, view_cnt, post_id)
values
(?, ?, ?, ?, ?)
2021-10-03 22:37:31.722 DEBUG 31972 --- [ main] org.hibernate.SQL :
update
comment
set
content=?,
member_id=?,
post_id=?
where
id=?
- member, comment, post 각 1개씩 save했으니, insert 쿼리 3번 나감
- select 쿼리가 안나가는 이유는 1차 캐시에서 조회했기 때문.
- update 쿼리는??? 뭐임?? 업데이트 되는 파라미터를 확인해 보자.
- p6spy 라이브러리를 사용하면 sql의 파라미터를 볼 수 있음.
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'
- 쿼리를 다시 살펴보자.
- 연관관계 매핑 후 save 하였기만 insert Query에는 연관관계가 매핑이 되어있지 않다.
- FK가 다 null로 입력되어 있다. 음..
- 이후에 comment객체만 업데이트 된다.
insert
into
member
(email, name, password, user_id, member_id)
values
(?, ?, ?, ?, ?)
insert into member (email, name, password, user_id, member_id) values ('123@navber.com', 'memberA', '123465!', 'abc123', 1);
2021-10-03 22:46:16.844 DEBUG 26460 --- [ main] org.hibernate.SQL :
insert
into
comment
(content, member_id, post_id, id)
values
(?, ?, ?, ?)
insert into comment (content, member_id, post_id, id) values ('comment_content', 1, NULL, 2);
2021-10-03 22:46:16.845 DEBUG 26460 --- [ main] org.hibernate.SQL :
insert
into
post
(content, member_id, title, view_cnt, post_id)
values
(?, ?, ?, ?, ?)
insert into post (content, member_id, title, view_cnt, post_id) values ('content123', 1, 'title', NULL, 3);
2021-10-03 22:46:16.846 DEBUG 26460 --- [ main] org.hibernate.SQL :
update
comment
set
content=?,
member_id=?,
post_id=?
where
id=?
2021-10-03 22:46:16.847 INFO 26460 --- [ main] p6spy : #1633268776847 | took 0ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/blog
update comment set content=?, member_id=?, post_id=? where id=?
update comment set content='comment_content', member_id=1, post_id=3 where id=2;
- 아 이유는 간단했따.
- save 순서가
- member -> comment -> post
- comment를 save하는 시점에 post가 영속화 되어있지 않음.
memberService.joinMember(member);
commentService.saveComment(comment);
postService.savePost(post);
- save(영속화) 순서를 바꿔보자.
- member -> post -> comment
memberService.joinMember(member);
postService.savePost(post);
commentService.saveComment(comment);
- 영속화 순서 변경 이후 딱 insert 쿼리 3개만 나간다.
insert
into
member
(email, name, password, user_id, member_id)
values
(?, ?, ?, ?, ?)
insert into member (email, name, password, user_id, member_id) values ('123@navber.com', 'memberA', '123465!', 'abc123', 1);
2021-10-03 23:28:52.233 DEBUG 29384 --- [ main] org.hibernate.SQL :
insert
into
post
(content, member_id, title, view_cnt, post_id)
values
(?, ?, ?, ?, ?)
insert into post (content, member_id, title, view_cnt, post_id) values ('content123', 1, 'title', NULL, 2);
2021-10-03 23:28:52.236 DEBUG 29384 --- [ main] org.hibernate.SQL :
insert
into
comment
(content, member_id, post_id, id)
values
(?, ?, ?, ?)
insert into comment (content, member_id, post_id, id) values ('comment_content', 1, 2, 3);
7. Comment 계층구조에 대한 검증 추가.
@Test
public void commentMappingTest(){
//given
Comment parent = createComment();
Comment child = new Comment("child");
//when
child.setParent(parent);
commentService.saveComment(parent);
commentService.saveComment(child);
em.flush();
em.clear();
//then
assertThat(child.getParent().getId()).isEqualTo(parent.getId());
}
8. 결과
- 전체 연관관계
- 현재까지 모든 테스트 통과 ㅋㅋㅋㅋ
9. GitHub : 211003 Domain Association
'Project > 블로그 게시판 만들기' 카테고리의 다른 글
로그인 처리 (0) | 2021.10.04 |
---|---|
회원가입 개발 (0) | 2021.10.04 |
댓글 도메인 개발 (0) | 2021.10.03 |
댓글 도메인 설계 (0) | 2021.10.03 |
게시글 도메인 개발 (0) | 2021.10.03 |
Comments