Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
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
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

도메인 모델, 연관관계 본문

Project/블로그 게시판 만들기

도메인 모델, 연관관계

Seung__ 2021. 10. 3. 23:58

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

 

엔티티 클래스 개발

1. 엔티티 클래스 개발 시 주의사항(Getter, Setter), 참고 이론적으로 Getter, Setter 모두 제공하지 않고, 꼭 필요한 별도의 메서드를 제공하는게 가장 이상적. Getter의 경우 모두 열어두는 것이 편리하

bsh-developer.tistory.com

 

 

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 


 

GitHub - bsh6463/blog

Contribute to bsh6463/blog development by creating an account on GitHub.

github.com

 

'Project > 블로그 게시판 만들기' 카테고리의 다른 글

로그인 처리  (0) 2021.10.04
회원가입 개발  (0) 2021.10.04
댓글 도메인 개발  (0) 2021.10.03
댓글 도메인 설계  (0) 2021.10.03
게시글 도메인 개발  (0) 2021.10.03
Comments