@Entity
@Getter @Setter
@NoArgsConstructor
public class SubProject extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private ToyProject toyProject;
@Column
private String title;
@OneToOne(mappedBy ="subProject", fetch = FetchType.LAZY, orphanRemoval = true)
private KanbanBoard kanbanBoard;
@OneToMany(mappedBy = "subProject", orphanRemoval = true)
private List<Memoir> projectMemoirs = new ArrayList<Memoir>();
}
@Entity
@Getter
@Setter
@NoArgsConstructor
public class KanbanBoard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JsonIgnore
private SubProject subProject;
@OneToMany(mappedBy="kanbanBoard", orphanRemoval = true)
private List<KanbanList> kanbanLists=new ArrayList<KanbanList>();
@Column
private String boardName;
public void addKanbanList(KanbanList kanbanList){
kanbanList.setKanbanBoard(this);
this.kanbanLists.add(kanbanList);
}
public KanbanBoard(SubProject subProject, String boarName){
this.subProject=subProject;
this.boardName=boarName;
}
public List<InquireAllMemoirDto> inquireAllMemoirInSubProject(Long subProjectId){
SubProject subProject=subProjectRepository.findById(subProjectId).get();
List<InquireAllMemoirDto> inquireAllMemoirDtoList=new ArrayList<>();
for(Memoir memoir: subProject.getProjectMemoirs()){
InquireAllMemoirDto inquireAllMemoirDto=new InquireAllMemoirDto(memoir);
inquireAllMemoirDtoList.add(inquireAllMemoirDto);
}
return inquireAllMemoirDtoList;
}
@OneToOne 매핑 시, Fetch 전략을 LAZY로 설정해도 EAGER로 동작하는 경우가 있다. KanbanBoard 객체를 사용하는 시점도 없는데, LAZY로 설정했음에도 불구하고 조회 쿼리문이 발생한다.
문제 원인
연관관계의 주인이 호출할 때는 지연 로딩이 정삭적으로 동작하지만, 연관관계의 주인이 아닌 곳에서 호출한다면 지연 로딩이 아닌 즉시 로딩으로 동작한다.
SubProject과 KanbanBoard 중 주인은 현재 KanbanBoard이다. 주인인 곳에서 LAZY 조회하면 문제 없다. 왜냐하면 KanbanBoard는 SubProject의 id를 가지고 있다. 즉, fk를 가지고 있다.
반대로 SubProject에서 KanbanBoard를 LAZY으로 설정한 후 조회하면 EAGER Loading으로 조회한다. 주인이 아닌 SubProject는 KanbanBoard의 fk가 없기 때문에 나중에 Lazy Loading할 수 없어서 바로 조회한다. Lazy Loading 하려면 프록시 객체를 생성할 수 있어야 하는데, fk가 없으므로 생성하지 못한다. 프록시 객체는 null이 될 수 없다.
지연 로딩으로 설정이 되어있는 엔티티를 조회할 때는 프록시로 감싸서 동작하게 되는데, 프록시는 null을 감쌀 수 없기 때문에 이와 같은 문제점이 발생하게 된다. 즉, 프록시의 한계로 인해 발생하는 문제이다.
해결책
생각해보니 SubProject가 KanbanBoard의 정보를 가지고 있지 않아도 문제 없음을 깨달았다. 따라서, KanbanBoard가 주인인 단방향으로 @OneToOne 매핑하고, SubProject에서는 KanbanBoard를 지웠다.
참고
[JPA] OneToOne 양방향 매핑과 LazyLoading
[JPA] @OneToOne에서 Fetch 전략을 Lazy로 설정했을때 발생하는 이슈
'Programming > JPA' 카테고리의 다른 글
변경 감지와 병합 (0) | 2022.04.13 |
---|