순환참조
- 참조하는 대상이 서로 물려 있어 참조할 수 없게 되는 현상
- JPA에서 양방향으로 연결된 엔티티를 그대로 조회하는 경우, 서로의 정보를 순환하면서 조회하다가 stackoverflow 발생
엔티티 직접 노출 AS-IS
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ReserveShop {
@Id
@GeneratedValue
@Column(name = "reserve_shop_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reserve_id") // FK 설정, 연관관계 주인
private Reserve reserve;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "shop_id")
...
}
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Reserve {
@Id
@GeneratedValue
@Column(name = "reserve_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id") // FK 설정, 연관관계 주인
private Member member;
@OneToMany(mappedBy = "reserve", cascade = CascadeType.ALL)
private List<ReserveShop> reserveShops = new ArrayList<>();
...
}
- ReserveShop 클래스와 Reserve는 양방향 관계(N:1)
import detailing.reservation.domain.*;
import detailing.reservation.repository.ReserveRepository;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequiredArgsConstructor
public class ReserveApiController {
private final ReserveRepository reserveRepository;
@GetMapping("/api/v1/reserves")
public List<Reserve> reserveV1(){
List<Reserve> all = reserveRepository.findAllByString(new ReserveSearch());
for (Reserve reserve : all) {
reserve.getMember().getName();
List<ReserveShop> reserveShops = reserve.getReserveShops();
reserveShops.stream().forEach(r -> r.getShop().getName()); // Lazy 강제 초기화
}
return all;
}
...
}
- 순환 참조 방지를 위해 Lazy 강제 초기화 진행
- Lazy 강제 초기화를 진행하였지만, API 호출 시 reserveShop -> reserve -> reserveShop -> reserve와 같이 순환 참조 발생
엔티티 직접 노출 TO-BE
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ReserveShop {
@Id
@GeneratedValue
@Column(name = "reserve_shop_id")
private Long id;
@JsonIgnore // 추가
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reserve_id") // FK 설정, 연관관계 주인
private Reserve reserve;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "shop_id")
...
}
- 순환 참조가 발생하는 엔티티 @JsonIgnore 추가
- @JsonIgnore를 추가함으로써 순환 참조 문제 해결
- 해당 어노테이션 사용 시 JSON 데이터에 해당 프로퍼티는 null로 들어감. 즉 데이터에 아예 포함시키지 않음
다른 해결법
1. @JsonManagedReference, @JsonBackReference 사용
- 순환참조를 방어하기 위한 어노테이션
- @JsonManagedReference를 연관 관계 주인의 반대편에 선언
- @JsonBackReference를 연관 관계 주인(외래키가 있는곳)에 선언
2. DTO 사용
- 필요한 데이터만 옮겨담아 return 하여 순환참조 방어 가능
- 다음 게시글에 작성 예정
'개발진행목록 > 셀프 세차장 예약 서비스' 카테고리의 다른 글
예약 조회 API 개발 (0) | 2024.10.24 |
---|---|
회원 관련 API 개발 (0) | 2024.10.17 |
설계 (0) | 2024.09.20 |