TIL

연관관계 매핑 프로젝트에 적용하기(코드)

haedal-uni 2022. 11. 16. 21:38
728x90

이전에 연관관계에 관해 글을 정리했다. 

*이론이나 궁금해서 공부한 것들은 git blog에 정리하고 프로젝트 관련은 전부 tistory에 정리 중

 

이론을 알았으니 이제 본 프로젝트에 적용시켜본다. (이론 생략) 

 

*코드를 뜯어보면서 적는 것은 git에 정리했으니 이론과 같이 보려면 아래 링크로 들어가서 보면 된다.

GitBlog-연관관계 적용해보기

 

현 문제점 + 목표는 댓글이 0개인 글들을 모아서 따로 보여주는데

페이지 별로 따로 나와서 어떤 페이지에서는 댓글이 0개인 글이 없어서 아예 없는 것으로 나오기도 한다.

그래서 전체 게시글 중에서 오래 된 순으로 댓글이 0개인 글을 몇 개만 보여주는 것으로 수정하려고 한다.

 

참고로 기존 entity에서 Registry와 Comment는 매핑이 되어있지 않으나 Comment에 Registry data가 일부 들어가있다. 그래서 매핑 이후에는 관련된 컬럼들을 지울 예정이다.

 

 

일단 게시글과 댓글 entity를 다대일 양방향 매핑으로 진행했다.

한 게시글 당 여러 개의 댓글이므로 주인은 Comment이다보니 실제 db에도 Comment table에 매핑되는 fk값을 넣어주면 된다.

* Registry db에 Comment 값이 들어가지 않는 이유는 다대 일이기 때문에 db에서 여러 개의 데이터가 들어갈 수 없기 때문이다.

따라서 다대 일에서는 다가 주인일 수 밖에 없다.

 

 

 

Comment.java

@ManyToOne // 주이이인
@JoinColumn(name = "registry_id")
private Registry registry;

JoinColumn의 name은 내가 연결하고 싶은거 아무거나 적으면 된다.

 

 

Registry.java

@OneToMany(mappedBy = "registry")
@JsonIgnore
private List<Comment> comments = new ArrayList<>();

 

*mappedBy 설정이 잘못되면 에러뜬다.

여기서 mappedBy는 주인이 아닌 객체에 옵션을 부여하고 반대쪽 필드명을 적는 것이다.

(* private Registry registry의 registry)

 

 

 

 

여기서 중요한 것은 초기화를 해주는 것이다.

또한 무한 루프에 걸리지 않게 세팅해주는 것도 중요하다.

 

Comment.java

public void setRegistry(Registry registry) {
    // 기존에 연결된게 있을 경우 초기화
    if(this.registry != null) {
        this.registry.getComments().remove(this);
    }
    this.registry = registry;

    // 무한 루프 안걸리게 하기
    if (! registry.getComments().contains(this)) {
        registry.addComment(this);
    }
}

 

Registry.java

public void addComment(Comment comment) {
    this.comments.add(comment);

    // 무한 루프 안걸리게 하기
    if(comment.getRegistry() != this) {
        comment.setRegistry(this);
    }
}

 

 

 

 

이대로 실행하면 db에 값이 들어오지 않는다.

비즈니스 로직에 코드를 넣어줘야 한다.

 

CommentServiceImpl.java

@Transactional
public Comment setComment(CommentDto commentDto) {
   Comment comment = new Comment(commentDto);
   commentRepository.save(comment);
   return comment;
}

⬇️

@Transactional
public Comment setComment(CommentDto commentDto) {
   Comment comment = new Comment(commentDto);
   Registry registry = registryRepository.findById(comment.getRegistryId()).get();
   comment.setRegistry(registry);
   commentRepository.save(comment);
   return comment;
}

 


Test를 하면서 나타났던 오류

1. Transaction

could not initialize proxy - no Session 에러가 떠서 확인해보니

@Transactional 어노테이션을 붙이지 않아서 생긴 것이었다.

 해당 메서드가 시작하고 끝날 때까지 같은 세션이 유지 되게 @Transactional 을 붙여준다.

 

 

기본적으로 Hibernate에서 지연 로딩을 구현하기 위해 실제 객체가 아닌 Proxy 객체를 사용함으로써

실제 객체를 메모리에 로드하지 않아 자원을 절약한다.

Session은 영속성 컨텍스트를 관리하는 엔티티 매니저라고 생각하면 된다.

즉, 영속 상태인 Proxy 객체에 실제 데이터를 불러오려고 초기화를 시도하지만

Session이 close되어서 준영속 상태가 되어 값을 가져올 수가 없어 발생한 오류임을 알 수 있다.

다시말해서, 지연 로딩을 하려면 해당 객체는 무조건 영속성 컨텍스트에서 관리해야 한다.

 

출처 : [Spring Boot] 테스트 코드에서 getOne 또는 getById쓸 시 LazyInitializationException : could not initialize proxy - no Session

 

 

 

2. AUTO

    @Test
    void commentSave() {
        Registry registry = new Registry();
        registry.setIdx(1L);
        registry.setNickname("coco");
        registry.setTitle("안녕하세요");
        registry.setMain("hi");

        registryRepository.save(registry);

        Comment comment = new Comment();
        comment.setIdx(1L);
        comment.setComment("❤️🧡💛💚💙💜🤎🖤");
        comment.setNickname("우헤헤");
        comment.setRegistryId(5L);
        comment.setRegistryNickname("pupp");

        comment.setRegistry(registry);
        commentRepository.save(comment);


        System.out.println("comment.getComment() :   " + comment.getComment());
        Comment savedComment = commentRepository.findById(1L).get();
    }

 

코드 제일 아래 savedComment에서 에러가 떴다. 값이 존재하지 않는다고 떴다.

그런데 분명 나는 1L로 값을 줬는데 왜 값이 없다고 뜨는건지 잘 몰랐다.

 

값을 저장하는 것에 문제가 있었다면 이 전에 작성했던 각각의 test에서 실패가 떴거나

이 전까지 db에 저장하는 부분에서 오류가 났어야 했다.

 

그런데 해당 코드만 문제가 떠서 이유를 몰랐다.

로그를 살펴보니 id값을 2로 저장하고 있었다. (????)

 

그래서 2L로 바꿔서 실행을 하니 값이 떴다.

그 이유를 몰라서 검색해봤지만 답이 될만한 정보는 없었다.

 

1L로 저장했는데 AUTO로 설정해서 알아서 2L로 넘어가나 싶었지만

1L도 없는데 왜 2L로 넘어가는지 의문이 들었다.

일단 AUTO 대신에 IDENTITY로 변경하고 실행했더니 결과는 성공했다.

 

@GeneratedValue(strategy = GenerationType.AUTO)

⬇️

@GeneratedValue(strategy = GenerationType.IDENTITY)

 

그 이유를 파악하기 위해 AUTO와 IDENTITY에 대해서 좀 더 알아봤다.

 

git blog에 정리한 글 보러가기 >> Git blog

 

코드 전체 보기  >> Git-adme

 

 

728x90

'TIL' 카테고리의 다른 글

연관관계 글 정리 완료  (0) 2022.11.29
연관관계 매핑 후 수정  (0) 2022.11.20
Git blog 이미지 크기 수정하기(chirpy)  (0) 2022.10.26
고객센터 틀 작성  (0) 2022.10.20
Security  (0) 2022.10.11