티스토리 뷰
Hibernate Basic : Bi-directional 관계 Entity의 JSON 재귀적 호출 해결
Korean Eagle 2020. 6. 11. 18:111. 이 포스트는 하이버네이트의 양방향 One to Many나 Many to Many관계에서 발생하는 무한재귀호출에 대한 것이다.
2. 이 문제는 jackson이 객체를 네트워크로 보낼 데이터로 변환(serialization)하면서 circle이 발생하는 entity를 정상적으로 처리하지 못하여 발생하는 문제이다.
2-1 반드시 내부적으로 순환관계가 있어야 발생하는 문제이다. 단방향으로 정의하면 문제가 생기지 않는다.
2-2 환자와 진료데이터를 가지고 설명한다.
2-2-1 환자는 여러 진료데이터를 가지고 있고,
2-2-2 진료데이터는 단 한 명의 환자 정보를 가지고 있다.
3. 환자와 진료데이터 entity
3-1 환자 Entity
3-1-1 중요한 것은 환자와 데이터가 서로에 대한 참조변수를 가지고 있는 부분이다.
package pe.pilseong.clinicals.entity;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Entity
@Table(name = "patient")
@Data
@EqualsAndHashCode(callSuper=false)
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
public class Patient extends AbstractEntity {
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "age")
private Integer age;
@OneToMany(mappedBy = "patient")
private List<ClinicalData> data;
public void addClinicalData(ClinicalData data) {
if (this.data == null) {
this.data = new ArrayList<>();
}
this.data.add(data);
data.setPatient(this);
}
}
3-2 진료데이터 entity
package pe.pilseong.clinicals.entity;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.hibernate.annotations.CreationTimestamp;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "clinicaldata")
@Getter
@Setter
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
public class ClinicalData extends AbstractEntity {
@ManyToOne
@JoinColumn(name = "patient_id", nullable = false)
private Patient patient;
@Column(name = "component_name")
private String componentName;
@Column(name = "component_value")
private String componentValue;
@Column(name = "measured_date_time")
@CreationTimestamp
private Timestamp measuredDatetime;
@Override
public String toString() {
return "ClinicalData [componentName=" + componentName +
", componentValue=" + componentValue + ", measuredDatetime=" +
measuredDatetime + "]";
}
}
3-3 의미없는 @MappedSuperclass 완결성을 위해 붙여 놓는다.
package pe.pilseong.clinicals.entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import lombok.Data;
@MappedSuperclass
@Data
public class AbstractEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
4. 이 문제를 해결하기 위한 방법은 여러가지가 있는데, 2가지의 임기응변과 3가지의 해결책이 있다.
4-0 5가지 방법이 있는데 예제를 생략한 것은 굳이 알 필요가 없어서 이다.
4-0-1 임기응변은 @JsonIgnore만으로 족하고 해결책으로는 @JsonIdentityInfo만으로 족하다.
4-0-2 다른 방식으로 쓰여진 코드가 있으면 인터넷에 찾아보면 되는데 어려운 내용이 아니다.
4-1 2가지 임기응변은 한쪽 Entity에서 다른 쪽의 Enity를 끊어버리는 것으로 한쪽에만 데이터가 표시된다.
4-1-1 @JsonIgnore과 @JsonManagedReference, @JsonBackReference 쌍이 있다.
4-1-1-1 @JsonIgnore은 속성에 붙이는 것으로 붙여진 속성은 Jackson에서 Json으로 변환하지 않는다.
4-1-1-2 @JsonManagedReference, @JsonBackReference 쌍도 동일한 방식으로 동작한다. 같이 써야 한다.
4-1-1-2-1 다만, 어느 Entity를 보출할지를 정할 수 있을 뿐이다. 사실 아무런 의미가 없다.
4-1-1-2-2 @JsonManagedReference도 속성에 붙이는데 이 속성이 붙어 있으면 Json으로 변환이 된다.
4-1-1-2-3 @JsonBackReference는 수식되는 속성은 Json변환에서 제외된다.
4-1-5 @JsonManagedReference은 쓰지 않는 게 좋다. post로 데이터를 생성할 때 오류가 뜨는 경우가 있다.
4-2 3가지 해결책은 Serialization이 정상적으로 되도록 hint를 jackson에게 제공하는 것이다.
4-2-0 해결책이라는 말은 양쪽 모두 포함되는 정보를 모두 json으로 변환하기 때문이다.
4-2-1 별도의 DTO객체를 개발자가 작성하여 아예 문제가 생기지 않도록 한다. 제일 좋은 방법이다.
4-2-2 filter클래스를 작성하고 @JsonView를 사용한다. 좋은 방법이긴 한데 클래스를 생성해야 해서 귀찮다.
4-2-3 @JsonView가 귀찮으면 @JsonIdentityInfo를 사용하여 직렬화된 객체에 ObjectID를 박아버린다.
4-2-3-1 이 방법을 위의 코드에서 사용하고 있고,
4-2-3-2 이 annotation의 property는 속성은 위의 예제의 경우 기본값 id가 entity속성에 있으므로 사실 필요없다.
'Spring > Hibernate' 카테고리의 다른 글
Hibernate : @Lob (Large Object) (0) | 2020.07.28 |
---|---|
Hibernate : Hibernate DDL Auto (0) | 2020.07.27 |
Hibernate Advanced : 상속 매핑 - Mapped Superclass Strategy (0) | 2020.06.06 |
Hibernate Advanced : 상속 매핑 - Joined Table Strategy (0) | 2020.06.06 |
Hibernate Advanced : 상속 매핑 - Table Per Class Strategy (0) | 2020.06.06 |
- Total
- Today
- Yesterday
- 도커 개발환경 참고
- AWS ARN 구조
- Immuability에 관한 설명
- 자바스크립트 멀티 비동기 함수 호출 참고
- WSDL 참고
- SOAP 컨슈머 참고
- MySql dump 사용법
- AWS Lambda with Addon
- NFC 드라이버 linux 설치
- electron IPC
- mifare classic 강의
- go module 관련 상세한 정보
- C 메모리 찍어보기
- C++ Addon 마이그레이션
- JAX WS Header 관련 stackoverflow
- SOAP Custom Header 설정 참고
- SOAP Custom Header
- SOAP BindingProvider
- dispatcher 사용하여 설정
- vagrant kvm으로 사용하기
- git fork, pull request to the …
- vagrant libvirt bridge network
- python, js의 async, await의 차이
- go JSON struct 생성
- Netflix Kinesis 활용 분석
- docker credential problem
- private subnet에서 outbound IP 확…
- 안드로이드 coroutine
- kotlin with, apply, also 등
- 안드로이드 초기로딩이 안되는 경우
- navigation 데이터 보내기
- 레이스 컨디션 navController
- raylib
- Many-To-Many
- form
- hibernate
- 매핑
- 상속
- Angular
- 하이버네이트
- Spring Security
- MYSQL
- login
- Spring
- 외부파일
- crud
- XML
- WebMvc
- one-to-many
- Validation
- Rest
- 설정하기
- RestTemplate
- Security
- mapping
- one-to-one
- 자바
- spring boot
- 로그인
- jsp
- 스프링부트
- 스프링
- 설정