Jpa 순환 참조 N + 1 문제

2024. 2. 27. 13:47Spring Boot

RestAPI를 만들려고 하던 중 이상한 오류가 발생 했다

 

 

 

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;


@RestController
@RequestMapping("mysql/rest")
public class my_rest_controller {
    
    @Autowired
    mySQLService service;

    
    @RequestMapping(path ="/{id}", method=RequestMethod.GET)
    public sqlEntity get_one_api() {
        return service.all_data().get(0);
    }

    @RequestMapping(path = "/", method=RequestMethod.GET)
    public List<sqlEntity> get_api() {
        return service.all_data();
    }
}

 

 

 

 

같은 값을 계속해서 출력하는 문제가 발생 한 것

 

 

    @GetMapping()
    public String all_data(Model model) {
        model.addAttribute("testList", service.all_data());
        return "db/mySQLBoard";
    }

 

 

 

 

 

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;
@Entity
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Table(schema = "sql_entity")
public class sqlEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String title;

    @Column
    private String data;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
    private List<sqlChild> childs = new ArrayList<>();

    sqlEntity(String title, String data) {
        this.title = title;
        this.data = data;
    }
}

 

JSON으로 바꾸지 않고

그냥 Model에 값을 넣을 때는 문제가 없어서

JSON 변환 과정의 문제인 줄 알았는데

 

 

사용하는 Class에 

@OneToMany로 매핑한 데이터가 문제였다.

 

 

 

이를 지우자 정상적으로 작동 했다.

 

 

 

 

좀 더 알아보니 이는 Jpa의 순환 참조 문제라고 한다.

Jackson은 Entity의 Getter 호출하고 직렬화를 이용해서 Json으로 변환하는데

 

이 과정에서 entity가 자신의 child에 값을 읽고

child는 다시 자신 부모 entity의 값을 읽고를 반복해서
무한하게 값을 읽게 된 것

 

해결 방안은

DTO를 하나 만들어서 필요한 데이터만 직접 return

 

참초하는 값에 @JsonIgnore을 붙이기

 

부모가 참조하는 필드에 @JsonManagedReference 붙이고 자식이 참조하는 필드에 @JsonBackReference 붙이기

 

부모 클래스이 @JsonIgnoreProperties 붙이기

 

양방향 매핑 끊기 등이 있다

 

    @JsonIgnore
    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
    private List<sqlChild> childs = new ArrayList<>();

 

 

'Spring Boot' 카테고리의 다른 글

Spring Boot Studying  (0) 2024.02.28
Authorization by Spring Security  (0) 2024.02.28
CORS  (1) 2024.02.24
Naver Login  (0) 2024.02.19
Web Socket By STOMP  (0) 2024.02.19