** 자기 공부용 및 암기용으로 포스팅하는 글 입니다.
** 정확한 정보는 보장 할 수 없으니, 참고용으로만 봐주시면 감사하겠습니다.
** 제 개인적인 생각 및 자기 암시목적으로 작성되기에 이점 양해 부탁드립니다.
들어가기에 앞서..
두 개의 테이블(Entity) 관계를 ManyToOne, OneToMany, OneToOne 을 사용하여 연결할 때 양방향 관계에서 무한 순환 참조가 되는 현상을 겪은 적이 있다. 이를 해소하기 위해 사용하였던 어느테이션을 개념 정리할 겸 포스팅을 작성하기로 했다.
★ 알아볼 내용
- Entity에서 양방향 관계를 설정 할 때, 주의 해야 할 것
- @Jsonmanagedreference
- @Jsonbackreference
- 응용 부분
내용 설명
1. Entity에서 양방향 관계 설정 할 때, 주의해야 할 것
프로그래밍을 진행하면서 JPA에 양방향으로 연결된 엔티티를 JSON 형태로 직렬화 하는 과정이 필요할 때가 있다.
이 작업을 진행하다가 서로의 정보를 계속 순환해서 결국은 StackOverflowError 를 발생시키는 현상이 종종 발생한다.
Spring Boot에서 @ResponseBody를 구한하는 중에 Object를 Json형태를 변환 시 사용되는 Entity의 getter에서 직렬화를 이용하여 JSON형태로 변환해준다.
이 때 양방향으로 연결되어있는 Entity를 조회할 시 순환 참조를 막을 수 있는 소스가 제대로 구현이 되어 있지 않는다면은 무한으로 돌아 결국은 StackOverflowError가 발생하게 된다.
여기서 직렬화( Serialization ) 란
객체를 저장하거나 전송할 수 있는 형식으로 변경하는 과정을 의미한다.
java에서는 객체를 byte stream으로 변환하여 File/DB/Network를 통해 저장 및 전송할 수 있다.
JSON 직렬화의 경우는 객체를 JSON 형식의 문자열로 변환한다는 의미.
2. @Jsonmanagedreference
객체의 양방향 관계를 처리할 때 사용되는 어노테이션이다.
이 어노테이션은 주로 부모 객체의 필드에서 사용되며, 객체의 양방향 관계에서 자식 객체를 직렬화할 때 참조를
관리한다.
@Getter
@Setter
@Entity(name = "MEMBER")
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long memberId;
@Column(nullable = false, length = 30)
private String name;
@Column(nullable = false,updatable = false,length = 100)
private String email;
@Column(nullable = false,length = 100)
private String password;
@JsonManagedReference
@OneToMany(mappedBy = "member")
private List<Question> questions = new ArrayList<>();
//.... 이하 생략
3. @Jsonbackreference
객체의 양방향 관계를 처리할 때 사용되는 어노테이션이다.
이 어노테이션은 자식 객체의 필드에서 사용되며 @JsonManagedReference와 쌍을 이룬다.
또한 부모 객체를 참조할 때 이 필드를 직렬화에서 제외시키도록 한다.
두 개의 어노테이션을 적절하게 사용할 경우 무한 재귀를 방지할 수 있다.
@Getter
@Setter
@Entity
public class Question extends Auditable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long questionId;
@Column(nullable = false, length = 100)
private String title;
@Column(nullable = false, columnDefinition = "TEXT")
private String content;
@ManyToOne
@JsonBackReference
@JoinColumn(name = "MEMBER_ID")
private Member member;
//....
4. 응용 부분
이전에 @Jsonmanagedreference와 @Jsonbackreference에 대해 모를 때, 무한 순환 참조를 막기 위해 아래와 같이 소스코딩을 진행 했었다..
@Getter
@Setter
@Entity
@NoArgsConstructor
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long boardId;
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
public void setMember(Member member) {
if (!member.getBoards().contains(this)) {
member.getBoards().add(this);
}
this.member = member;
}
}
@Getter
@Setter
@Entity
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long memberId;
@OneToMany(mappedBy = "member")
private List<Board> boards = new ArrayList<>();
public void setBoard(Board board){
this.boards.add(board);
if(board.getMember() != this){
board.setMember(this);
}
}
}
직접 setBoard와 setMember 메서드를 만들어 직접 순환 참조를 방지하였는데 이 대신 이번에 배운 어노테이션을 사용하면 아래와 같이 간단하게 무한 순환 참조를 방지 할 수 있다.
@Getter
@Setter
@Entity
@NoArgsConstructor
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long boardId;
@ManyToOne
@JsonBackReference
@JoinColumn(name = "MEMBER_ID")
private Member member;
}
@Getter
@Setter
@Entity
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long memberId;
@OneToMany(mappedBy = "member")
@JsonManagedReference
private List<Board> boards = new ArrayList<>();
}
'Spring 지식 및 공부' 카테고리의 다른 글
[Spring] Spring JPA - 앤티티 간 연관 관계 매핑 OneToMany & ManyToOne & OneToOne & ManyToMany (0) | 2024.07.16 |
---|---|
[Spring] Spring JPA - 영속성 컨텍스트 ( Persistence Context ) (0) | 2024.07.11 |
[Spring] Spring JPA - 기본개념 (0) | 2024.07.09 |
[Spring] Spring MVC (0) | 2024.07.08 |
[Spring] 자주 사용하는 어노테이션(Annotation) 정리 (0) | 2024.07.06 |