Entity Graph란?
Entity Graph는 JPA(Java Persistence API)에서 제공하는 기능으로, 엔티티 객체를 가져올 때 연관된 엔티티 객체들을 함께 가져오는 방법을 지정하는 기능입니다. 일반적으로 JPA에서는 지연 로딩(lazy loading) 방식을 사용하여 연관된 엔티티 객체를 필요할 때 가져옵니다. 그러나 이 경우에는 연관된 엔티티 객체를 가져오는 쿼리가 지연 로딩 수행 시 매번 실행되어 성능상의 이슈가 발생할 수 있습니다.
Entity Graph는 이러한 성능상의 이슈를 해결하기 위해, 한 번의 쿼리로 필요한 모든 엔티티 객체를 함께 가져오기 위한 방법을 제공합니다. 따라서 연관된 엔티티 객체를 여러 번 가져오지 않아도 되므로 성능상의 이점을 얻을 수 있습니다.
Entity Graph를 사용할 때는, 엔티티 클래스에 @NamedEntityGraph 어노테이션을 사용하여 Entity Graph를 정의하고, 필요에 따라서 @EntityGraph 어노테이션을 사용하여 사용할 Entity Graph를 지정합니다. 이렇게 Entity Graph를 사용하면, 지연 로딩이 수행되는 대신, 한 번의 쿼리로 필요한 모든 엔티티 객체를 가져올 수 있습니다.
Entity Graph를 사용하면 성능상의 이점을 얻을 수 있으며, 복잡한 엔티티 간의 관계를 가진 객체를 가져올 때 유용합니다.
Entity Graph를 사용하는 방법
Named Entity Graph와 Dynamic Entity Graph 소개
Named Entity Graph는 미리 정의된 Entity Graph를 사용하는 방식으로, @NamedEntityGraph 어노테이션을 사용하여 엔티티 클래스에 정의합니다. Named Entity Graph를 사용하면 애플리케이션 코드에서 엔티티 객체를 가져올 때 해당 Named Entity Graph를 지정하면, 미리 정의된 Entity Graph에 따라서 필요한 엔티티 객체를 함께 가져올 수 있습니다. Named Entity Graph는 엔티티 클래스에서 정의하므로, 여러 곳에서 사용할 수 있어 재사용성이 높습니다.
Dynamic Entity Graph는 런타임에 동적으로 Entity Graph를 생성하여 사용하는 방식으로, EntityManager의 createEntityGraph 메소드를 사용하여 생성합니다. Dynamic Entity Graph는 엔티티 객체를 가져올 때마다 매번 새로운 Entity Graph를 생성하여 사용하므로, Named Entity Graph보다는 유연성이 높습니다. 예를 들어, 동적으로 엔티티 객체를 가져올 때마다 필요한 엔티티 객체를 다르게 가져오는 경우에 유용합니다.
Named Entity Graph를 사용하는 방법과 예제
Named Entity Graph를 사용하기 위해서는, 엔티티 클래스에서 @NamedEntityGraph 어노테이션을 사용하여 Entity Graph를 정의해야 합니다. Entity Graph는 엔티티 클래스의 필드나 연관관계를 이용하여 정의할 수 있으며, 다음은 Named Entity Graph를 사용하는 방법과 예제입니다.
- Named Entity Graph 정의하기
@Entity
@NamedEntityGraph(name = "Book.author", attributeNodes = @NamedAttributeNode("author"))
public class Book {
// ...
}
위 예제에서는 Book 클래스에 "Book.author"라는 이름의 Named Entity Graph를 정의하였습니다.
"Book.author" Entity Graph는 Book 클래스에 있는 author 필드와 연관된 엔티티 객체를 함께 가져올 수 있습니다.
- Named Entity Graph 사용하기
@EntityGraph("Book.author")
List<Book> books = entityManager.createQuery("SELECT b FROM Book b", Book.class)
.setHint("javax.persistence.fetchgraph", entityManager.getEntityGraph("Book.author"))
.getResultList();
위 예제에서는 EntityManager의 createQuery 메소드를 사용하여 엔티티 객체를 조회한 후, setHint 메소드를 사용하여 "javax.persistence.fetchgraph" 힌트를 지정하였습니다. 이때, "javax.persistence.fetchgraph" 힌트의 값으로 entityManager.getEntityGraph("Book.author")를 지정함으로써, "Book.author" Entity Graph를 사용하여 필요한 엔티티 객체를 함께 조회할 수 있습니다.
Named Entity Graph를 사용하면, 애플리케이션 코드에서 엔티티 객체를 조회할 때 매번 Entity Graph를 정의할 필요가 없으므로, 코드의 가독성이 높아지고, 재사용성이 높아집니다.
Dynamic Entity Graph를 사용하는 방법과 예제
Dynamic Entity Graph를 사용하기 위해서는 EntityManager의 createEntityGraph 메소드를 사용하여 Entity Graph를 동적으로 생성해야 합니다. Dynamic Entity Graph는 엔티티 객체를 조회할 때마다 매번 생성하여 사용하므로, Named Entity Graph보다는 유연성이 높습니다. 다음은 Dynamic Entity Graph를 사용하는 방법과 예제입니다.
- Dynamic Entity Graph 생성하기
EntityGraph<Book> entityGraph = entityManager.createEntityGraph(Book.class);
entityGraph.addAttributeNodes("author");
위 예제에서는 EntityManager의 createEntityGraph 메소드를 사용하여 Book 클래스에 대한 Entity Graph를 생성하였습니다. 이때, addAttributeNodes 메소드를 사용하여 Book 클래스의 author 필드와 연관된 엔티티 객체를 함께 조회하도록 설정하였습니다.
- Dynamic Entity Graph 사용하기
TypedQuery<Book> query = entityManager.createQuery("SELECT b FROM Book b", Book.class)
.setHint("javax.persistence.fetchgraph", entityGraph);
List<Book> books = query.getResultList();
위 예제에서는 EntityManager의 createQuery 메소드를 사용하여 엔티티 객체를 조회한 후, setHint 메소드를 사용하여 "javax.persistence.fetchgraph" 힌트를 지정하였습니다. 이때, "javax.persistence.fetchgraph" 힌트의 값으로 위에서 생성한 entityGraph를 지정함으로써, 동적으로 생성한 Entity Graph를 사용하여 필요한 엔티티 객체를 함께 조회할 수 있습니다.
Dynamic Entity Graph를 사용하면, 애플리케이션 코드에서 Entity Graph를 동적으로 생성할 수 있으므로, 동적인 요구 사항에 더욱 적합합니다. 그러나 Named Entity Graph보다는 코드의 복잡도가 높아질 수 있습니다.
Entity Graph의 성능 향상 기능
FetchType 설정과 Join Fetch를 이용한 쿼리 최적화
Entity Graph를 사용하여 쿼리 최적화를 하기 위해서는, FetchType 설정과 Join Fetch를 이용할 수 있습니다.
- FetchType 설정
Entity Graph에서 FetchType을 설정하여, 연관된 엔티티 객체를 즉시(EAGER) 로딩하도록 설정할 수 있습니다. 이를 통해, 연관된 엔티티 객체를 함께 가져오기 위해 추가적인 쿼리가 발생하지 않도록 할 수 있습니다. 따라서, FetchType을 적절히 설정함으로써 성능상의 이점을 얻을 수 있습니다.
@Entity
@NamedEntityGraph(name = "Book.author", attributeNodes = @NamedAttributeNode(value = "author", subgraph = "author.country"))
public class Book {
@ManyToOne(fetch = FetchType.LAZY)
private Author author;
// ...
}
위 예제에서는 Book 클래스의 author 필드의 FetchType을 LAZY로 설정하였으며, Entity Graph에서 subgraph를 사용하여 author 필드와 연관된 엔티티 객체 중, Country 엔티티 객체도 함께 가져올 수 있도록 설정하였습니다.
- Join Fetch
Join Fetch는 SQL 조인을 사용하여, 연관된 엔티티 객체를 즉시(EAGER) 로딩하도록 설정하는 방법입니다. Join Fetch를 사용하면, 쿼리 최적화를 위해 필요한 엔티티 객체들을 한 번의 쿼리로 함께 가져올 수 있습니다.
List<Book> books = entityManager.createQuery("SELECT b FROM Book b JOIN FETCH b.author", Book.class)
.getResultList();
위 예제에서는 createQuery 메소드에 JOIN FETCH 구문을 추가하여, Book 클래스와 Author 클래스의 조인을 통해 즉시 로딩을 수행하도록 설정하였습니다.
FetchType 설정과 Join Fetch를 사용하면, 연관된 엔티티 객체를 함께 조회할 때 성능상의 이점을 얻을 수 있습니다. 다만, 적절한 사용을 위해서는 데이터베이스 스키마와 엔티티 클래스의 매핑 관계를 고려하여 적절한 방법을 선택해야 합니다.
Batch Size 설정과 Subgraph를 이용한 성능 최적화
Entity Graph를 사용하여 쿼리 최적화를 하기 위해서는, Batch Size 설정과 Subgraph를 이용할 수 있습니다.
- Batch Size 설정
Batch Size는 연관된 엔티티 객체를 한 번에 일정 개수씩 가져오도록 설정하는 방법입니다. 이를 통해, 쿼리 실행 시 많은 수의 쿼리 호출을 줄이고, 성능을 최적화할 수 있습니다.
@Entity
@BatchSize(size = 10)
public class Author {
// ...
}
위 예제에서는 Author 클래스에 @BatchSize 어노테이션을 사용하여, 연관된 엔티티 객체를 10개씩 가져오도록 설정하였습니다.
- Subgraph
Subgraph는 Entity Graph에서 다시 Entity Graph를 정의할 수 있는 기능입니다. 이를 사용하여, 연관된 엔티티 객체를 조금 더 세밀하게 로딩할 수 있습니다.
@Entity
@NamedEntityGraph(name = "Book.author", attributeNodes = @NamedAttributeNode(value = "author", subgraph = "author.country"))
public class Book {
// ...
}
@Entity
public class Author {
@ManyToOne(fetch = FetchType.LAZY)
private Country country;
// ...
}
@Entity
public class Country {
// ...
}
위 예제에서는 Book 클래스에서 author 필드와 연관된 엔티티 객체 중, Country 엔티티 객체도 함께 가져오도록 Entity Graph를 정의하였습니다. 이때, Author 클래스에서 country 필드와 연관된 엔티티 객체도 함께 가져오기 위해, Subgraph를 사용하여 Country 엔티티 객체에 대한 Entity Graph를 다시 정의하였습니다.
Batch Size 설정과 Subgraph를 사용하면, 연관된 엔티티 객체를 적절한 크기로 일괄적으로 가져올 수 있으며, 세밀한 로딩 제어도 가능합니다. 이를 통해, 쿼리 실행 속도를 개선하고 성능을 최적화할 수 있습니다.
Entity Graph 사용 시 주의할 점
Fetch Depth 설정을 통한 무한 재귀 호출 방지
Fetch Depth 설정을 사용하여 무한 재귀 호출을 방지할 수 있습니다. Fetch Depth는 연관된 엔티티 객체를 가져오는 깊이를 제한하는 방법입니다. 연관된 엔티티 객체를 가져오는 깊이를 제한함으로써, 무한 재귀 호출이 발생하는 상황을 방지할 수 있습니다.
@Entity
public class Book {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
@Fetch(FetchMode.JOIN)
@FetchProfile(fetchOverrides = {
@FetchProfile.FetchOverride(entity = Author.class, association = "country", mode = FetchMode.JOIN)
})
@FetchDepth(value = 1)
private Author author;
// ...
}
위 예제에서는 Book 클래스에서 author 필드와 연관된 엔티티 객체 중, Country 엔티티 객체에 대해 Fetch Depth를 1로 설정하였습니다. 이를 통해, author 필드와 연관된 엔티티 객체를 가져올 때, Country 엔티티 객체는 한 단계 깊이까지만 가져오도록 설정됩니다.
Fetch Depth 설정을 적절하게 사용하면, 무한 재귀 호출을 방지할 수 있으며, 성능을 향상시킬 수 있습니다. 다만, Fetch Depth를 지나치게 작게 설정하면, 원하는 데이터를 가져오지 못할 수 있으므로, 적절한 값을 선택하는 것이 중요합니다.
Entity Graph와 JPQL 힌트의 관계 설명
Entity Graph와 JPQL 힌트는 모두 쿼리 최적화를 위해 사용되는 기능입니다. Entity Graph는 애플리케이션 코드에서 엔티티 객체를 조회할 때 사용하는 방법으로, 연관된 엔티티 객체를 즉시 로딩하거나 Fetch Depth를 설정하여 무한 재귀 호출을 방지할 수 있습니다. JPQL 힌트는 createQuery 메소드의 setHint 메소드를 사용하여 쿼리 실행 시 최적화 옵션을 지정하는 방법으로, 캐시 사용 여부나 조인 전략 등을 지정할 수 있습니다.
Entity Graph와 JPQL 힌트는 함께 사용될 수 있습니다. Entity Graph를 사용하여 최적화를 시도한 후, JPQL 힌트를 사용하여 더욱 세밀하게 최적화할 수 있습니다. 예를 들어, Entity Graph를 사용하여 연관된 엔티티 객체를 함께 조회하되, 쿼리 실행 시 캐시를 사용하도록 설정할 수 있습니다.
TypedQuery<Book> query = entityManager.createQuery("SELECT b FROM Book b", Book.class)
.setHint("javax.persistence.fetchgraph", entityGraph)
.setHint("javax.persistence.cache.retrieveMode", CacheRetrieveMode.USE)
.setHint("javax.persistence.cache.storeMode", CacheStoreMode.USE);
List<Book> books = query.getResultList();
위 예제에서는 EntityManager의 createQuery 메소드를 사용하여 엔티티 객체를 조회한 후, setHint 메소드를 사용하여 "javax.persistence.fetchgraph" 힌트와 "javax.persistence.cache.retrieveMode" 힌트, "javax.persistence.cache.storeMode" 힌트를 지정하였습니다. 이때, "javax.persistence.fetchgraph" 힌트는 Entity Graph를 사용하여 최적화를 수행하며, "javax.persistence.cache.retrieveMode" 힌트와 "javax.persistence.cache.storeMode" 힌트는 캐시 사용을 지정하여 성능을 더욱 개선합니다.
Entity Graph와 JPQL 힌트는 모두 쿼리 최적화를 위해 사용되는 기능이지만, 각각의 특징과 사용 방법을 이해하고 적절하게 사용해야 합니다.
정리
Entity Graph는 JPA 2.1부터 도입된 쿼리 최적화 기능입니다. Entity Graph를 사용하면, 애플리케이션 코드에서 엔티티 객체를 조회할 때, 필요한 엔티티 객체들만 즉시 로딩할 수 있습니다. 이를 통해, 쿼리 실행 속도를 개선하고 성능을 최적화할 수 있습니다.
Entity Graph의 장단점과 활용성은 다음과 같습니다.
장점:
- 쿼리 실행 속도 개선: Entity Graph를 사용하면, 쿼리 실행 시 필요한 엔티티 객체들만 함께 로딩할 수 있으므로, 불필요한 쿼리 호출을 줄여 쿼리 실행 속도를 개선할 수 있습니다.
- 성능 최적화: Entity Graph를 사용하여 쿼리 최적화를 수행하면, 애플리케이션의 성능을 최적화할 수 있습니다.
- 유연성: Named Entity Graph와 Dynamic Entity Graph를 사용하여, 엔티티 객체를 유연하게 로딩할 수 있습니다.
단점:
- 복잡성: Entity Graph를 사용하면, 애플리케이션 코드가 복잡해질 수 있습니다. 특히, Dynamic Entity Graph를 사용하면, 애플리케이션 코드가 더욱 복잡해질 수 있습니다.
- 유연성 제한: Entity Graph를 사용하여 최적화를 수행하면, 쿼리 실행 속도를 개선할 수 있지만, 성능 최적화에 대한 유연성이 제한될 수 있습니다.
활용성:
- 대용량 데이터 처리: 대용량 데이터를 처리하는 애플리케이션에서, Entity Graph를 사용하여 성능을 최적화할 수 있습니다.
- 연관된 엔티티 객체 조회: 연관된 엔티티 객체를 함께 조회할 필요가 있는 애플리케이션에서, Entity Graph를 사용하여 성능을 최적화할 수 있습니다.
Entity Graph는 쿼리 최적화를 위해 유용한 기능입니다. 하지만, 사용 방법과 장단점을 이해하고 적절하게 사용해야 합니다. Entity Graph를 사용하여 애플리케이션의 성능을 개선하고 최적화하는 것이 중요합니다.
'spring > study' 카테고리의 다른 글
[spring] 객체 지향 설계 5가지 원칙 - SOLID (0) | 2023.02.27 |
---|---|
[Spring] @IdClass에 대해서 알아보자 (0) | 2023.02.27 |
[Spring] @Embeddable에 대해서 알아보자 (0) | 2023.02.27 |
[Spring] @Data 에 대해서 알아보자 (0) | 2023.02.25 |
[Spring] Swagger에 대해서 알아보자 (0) | 2023.02.23 |