JAVA/Spring Boot

JPA) ManyToOne 양방향 관계 설정

주코식딩 2022. 6. 5. 17:22

JPA의 Foreign Key적용 방식 및 원리

게시글과 댓글을 Spring으로 구현을 해야하는 상황에서 게시글과 댓글을 Foreign Key로 양방향 참조를 할 예정이다.

 

board 와 comments는 1:N 관계이다. 우선 Board에는 OneToMany를 Comments에는 ManyToOne을 설정해야한다는 것은 직관적으로 알 수 있다.

그렇다면 이 관계의 주인은 누구일까?

하나뿐인 Board가 주인일 것 같지만 아이러니 하게도 Comments가 주인이다.

Comments는 Board가 없으면 존재할 수 없지만 반대는 가능하기 때문에 외래키는 Comments쪽에서 관리하는 것이 타당하다.

 

주인 연관관계를 자세히 알았으니 코드에 적용시켜야 한다.

JPA에서는 mappedBy를 통해 양방향 관계를 설정한다.

mappedBy를 쓰기전 OneToMany와 ManyToOne이 적혀있다면 그것은 양방향관계라고 할 수 없고 단방향 관계가 2번 설정되었다고 할 수 있다.

mappedBy는 주인이 아닌 객체에 작성하며 의미는 다음과 같다.

 

주인은 Comments고 Comments에서 Borad를 참조하고 있다.

(Board객체에 작성했다는 점을 유의하자)

@OneToMany( mappedBy = "board") 
private List<Comments> commentsList = new ArrayList<Comments>();

위 코드는 양방향 관계임을 명시하기만 했을 뿐이므로 실제로 두 객체를 연결시켜주는 함수를 만들어야 한다.

 

Comments가 생성되었을 때 Board를 꼭 참조하게 만들어야 한다. 그리고 Board또한 Comments를 참조해야한다.

public void addComments(Comments comments){
    comments.setBoard(this);
    this.commentsList.add(comments);
}
@PostMapping("/api/comments/{nid}")
public Comments createComments(@PathVariable Long nid, @RequestBody CommentsDto commentsDto){
    Comments comments = new Comments(commentsDto);
    Optional<Board> optionalBoard  = boardRepository.findById(nid);

    optionalBoard.ifPresent(board -> board.addComments(comments));
    return commentsRepository.save(comments);
}

이런식으로 코드를 작성하게 되면 Repository에서 Response값을 return하게 되는데 두 객체가 서로를 참조하고 있기 때문에 stackoverflow 에러를 발생한다.

그래서 일단은 함수의 return형식을 void로 바꿔 주었지만 게시글이나 댓글의 목록을 보여주기 위해서는 해결해야만 하는 문제다.

추가)  https://ojy9612.tistory.com/55 어노테이션으로 객체대신 아이디만 가져오게 했다.

 

결과 코드 요약

Board 객체

@Entity
public class Board ...{

    ...
    
    @OneToMany( mappedBy = "board") // 주인은 Comment고 comment에서 borad를 참조하고 있다.
    private List<Comments> commentsList = new ArrayList<Comments>();

    public void addComments(Comments comments){
        comments.setBoard(this);
        this.commentsList.add(comments);
    }
	...
}

 

Comments 객체

@Entity
public class Comments ...{

	...

    @Setter
    @ManyToOne
    private Board board;
    
    ...
}

 

CommentController

@PostMapping("/api/comments/{nid}")
public void createComments(@PathVariable Long nid, @RequestBody CommentsDto commentsDto){
    Comments comments = new Comments(commentsDto);
    Optional<Board> optionalBoard  = boardRepository.findById(nid);

    optionalBoard.ifPresent(board -> board.addComments(comments));
    commentsRepository.save(comments);
}

 

 

https://www.youtube.com/watch?v=brE0tYOV9jQ

https://www.youtube.com/watch?v=hsSc5epPXDs