EntityGraph 배우면서
NamedEntityGraph를 쓰는 방법과 그냥 EntityGraph에 attributePaths를 지정하는 방법, 두가지가 있다는 사실을 배웠다.
그러면서 든 생각은... 왜 궂이 NamedEntityGraph를 쓰는거지? 걍 attributePaths에다가 다 적으면 되는 거 아닌가? 였다.
그래서 AI한테 물어보니까 나름 실용적인 이유가 있었다.
예를 들어 이런 Entity가 있다 치면
@Entity(name = "Parent")
@Table(name = "parents")
public static class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child1;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child2;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child3;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child4;
}
attributePaths 만으로 적을려면 이렇게 적어야 한다.
@EntityGraph(attributePaths={"child1", "child2", "child3", "child4"})
@Query("SELECT p from Parent p where p.id = :id")
Parent getParentAdHocEntityGraph(Long id);
근데 NamedEntityGraph를 쓰면
@NamedEntityGraph(name = "Parent.children",
attributeNodes = {
@NamedAttributeNode("child1"),
@NamedAttributeNode("child2"),
@NamedAttributeNode("child3"),
@NamedAttributeNode("child4"),
}
)
@Entity(name = "Parent")
@Table(name = "parents")
public static class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child1;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child2;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child3;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child4;
public Long getId() { return this.id; }
}
나중에 한번에 묶어서 표현 할 수 있다.
@EntityGraph("Parent.children")
@Query("SELECT p from Parent p where p.id = :id")
Parent getParentNamedEntityGraph(Long id);
package com.entitygraph;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import jakarta.persistence.*;
import java.util.Optional;
import java.util.List;
import java.util.ArrayList;
@SpringBootApplication
public class EntityGraphTest {
// =========================
// entities
// =========================
@NamedEntityGraph(name = "Parent.children",
attributeNodes = {
@NamedAttributeNode("child1"),
@NamedAttributeNode("child2"),
@NamedAttributeNode("child3"),
@NamedAttributeNode("child4"),
}
)
@Entity(name = "Parent")
@Table(name = "parents")
public static class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// STUDY:
// OneToOne에서 이렇게 fetch=FetchType.LAZY를 해도 지연 로딩이 안되다는 사실 아셨나요?
//
// 이에 대해서는 https://devlog-wjdrbs96.tistory.com/432 참고
//
// 사실 여기서는 fetch = FetchType.LAZY를 빼는 것도 맞는거 같습니다.
// 그러면 신기하게도 각 child를 select 하는 대신에
// join을 쓰기 시작합니다. (왠지는 모르겠지만....)
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child1;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child2;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child3;
@OneToOne(mappedBy = "parent", fetch = FetchType.LAZY) private Child child4;
public Long getId() { return this.id; }
}
@Entity(name = "Child")
@Table(name = "children")
public static class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column private String name;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Parent parent;
}
@Component
public class Demo implements CommandLineRunner {
private final ParentRepo repo;
public Demo(ParentRepo repo) {
this.repo = repo;
}
@Override
public void run(String... args) {
System.out.println("================================");
Parent p = repo.saveAndFlush(new Parent());
System.out.println("================================");
repo.findById(p.getId());
repo.flush();
System.out.println("================================");
repo.getParentNamedEntityGraph(p.getId());
repo.flush();
System.out.println("================================");
repo.getParentAdHocEntityGraph(p.getId());
repo.flush();
}
}
public static void main(String[] args) {
SpringApplication.run(EntityGraphTest.class, args);
}
}
package com.entitygraph;
import org.springframework.data.jpa.repository.JpaRepository;
import com.entitygraph.EntityGraphTest.Parent;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.EntityGraph;
public interface ParentRepo extends JpaRepository<EntityGraphTest.Parent, Long> {
@EntityGraph("Parent.children")
@Query("SELECT p from Parent p where p.id = :id")
Parent getParentNamedEntityGraph(Long id);
@EntityGraph(attributePaths={"child1", "child2", "child3", "child4"})
@Query("SELECT p from Parent p where p.id = :id")
Parent getParentAdHocEntityGraph(Long id);
}