일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- getDateCellValue
- Protecle
- NamedEntityGraph
- getEntityGraph
- AuditingEntityListener
- yml
- mysql =
- +9:00
- MSSQL
- pooled-lo
- sendFractionalSeconds
- https
- apatch poi
- 티스토리챌린지
- fractional seconds
- createEntityGraph
- 오블완
- EmbeddedId
- spring boot
- mysql equal null
- MYSQL
- mysql not equal null
- RootGraph
- 1*1000
- @CreateDate
- @EntityListeners
- load order
- deserializer
- 버전 문자열 비교
- 운동해서 광명찾자
- Today
- Total
Hello
[JPA] 하위 그래프가 하위 그래프를 사용하는 방법 본문
아래구조와 동일한 객체 조회 시 lazy 로 동작해 n+1이 발생했다.
특정 쿼리문에서만 join을 사용해 관련된 테이블을 모두 조회해야할 필요가 있어, EntityGraph를 사용해 해결한 방법을 정리하고자 글을 작성합니다.
어떤 게임에서 건축물을 지을 때 해당 건축물은 여러 옵션을 갖고 있다.
건축물은 옵션을 가지며, 옵션은 실제 효과를 여러개 가질 수 있다. 옵션과 효과 객체를 n:m 매핑 테이블을 만들어 사용이 필요하다.
Building 조회시 Option, OptionEffect, Effect를 가진 join 쿼리 적용 방법
- @NamedEntityGraph 정의
@Entity
@Table(name = "tb_building")
@NamedEntityGraph(name = "building.option.effect", attributeNodes = {
@NamedAttributeNode(value = "option", subgraph = "option.effect")
},
subgraphs = {
@NamedSubgraph(name = "option.effect", attributeNodes = {
@NamedAttributeNode(value = "optionEffects", subgraph = "effects")}),
@NamedSubgraph(name = "effects", attributeNodes = {
@NamedAttributeNode(value = "effect")})
})
public class Building {...}
- String으로 EntityGraph 구문 작성
이 글에서는 JPQL 쿼리 작성과 Query Method 두가지 방법에 EntityGraph를 적용하는 내용을 작성합니다.
JPQL 쿼리 작성
@NamedEntityGraph 사용
(EntityManager-getEntityGraph(), createEntityGraph() , EntityGraph - addSubgraph())
1-1) 정의된 EntityGraph를 반환
EntityGraph<?> entityGraph = entityManager.getEntityGraph("building.option.effect");
1-2) 정의된 EntityGraph의 변경 가능한 복사본 반환
EntityGraph<?> entityGraph2 = entityManager.createEntityGraph("building.option.effect");
1-3) EntityGraph를 동적으로 생성하는 데 사용할 수 있는 변경 가능한 EntityGraph를 반환
EntityGraph<Building> entityGraph2 = entityManager.createEntityGraph(Building.class);
entityGraph2.addSubgraph("option")
.addSubgraph("optionEffects")
.addAttributeNodes("effect");
SessionImplementor unwrap = entityManager.unwrap(SessionImplementor.class);
RootGraph<?> entityGraph = GraphParser.parse(Building.class, "option(optionEffects(effect))", unwrap);
쿼리에 entityGraph 적용
@Transactional
public List<Building> entityManagerFindAllByString() {
return entityManager.createQuery("select b from Building b", Building.class)
.setHint("javax.persistence.fetchgraph", entityGraph)
.getResultList();
}
Query Method
public interface BuildingRepo extends JpaRepository<Building, Integer> {
1) 정의된 @NamedEntityGraph 사용
@EntityGraph(value = "building.option.effect")
List<Building> findAll();
2) 문자열 EntityGraph 작성
@EntityGraph(attributePaths = "option.optionEffects.effect")
List<Building> findAll();
}
결과
sql logging을 활성화 하고 쿼리를 실행하면 Hibernate가 Building, Option, OptionEffect, Effect 테이블을 조인하고 4개 엔터티에 매핑된 쿼리를 생성한 것을 확인할 수 있습니다.
EntityGraph 사용 시 주의 사항
위 쿼리는 EntityGraph의 단점을 보여주고 있습니다. 간단한 쿼리에 EntityGraph를 추가 하가해 여러 테이블을 조인하고 많은 컬럼을 읽는 SQL 쿼리가 생성되었습니다. EntityGraph 정의에 주의하고 생성된 SQL 문을 확인하는 것이 중요합니다.
+) OneToMany List 중복 제거
결과는 building id 1,2는 option 1001을 가집니다.
OneToMany관계로 매핑 시 mappedBy("option")로 찾아오는데 이때,
JPA는 building 1일때 option(id:1001), 2일때 option(id:1001) 각각 조회된 데이터가 2개의 option을 가진 것 처럼동작한다.
해결 방법
Set으로 선언해 중복 객체가 담기지 않도록 한다.
List 사용 시 Stream.distincet() 호출해 중복 제거한 리스트를 새로 만든다.
위 케이스의 경우 hibernate5 distinct 옵션 [hibernate.query.passDistinctThrough=false or select distinct x from x] 적용 안됨
+) OneToMany, ManyToMany 컬렉션 관계 fetch join with pagination
setFirstResult().setMaxResults() 이용 시 "쿼리 결과를 메모리에 올려 paging 작업을 어플리케이션 처리한다" 는경고가 발생한다.
-( JQPL 쿼리, Query Method )
참고 :
https://www.inflearn.com/questions/14663/fetch-join-%EC%8B%9C-paging-%EB%AC%B8%EC%A0%9C
https://softwarehut.com/blog/tech/no-lazyinitializationexception-use-jpa-2-1-entity-graph
https://www.baeldung.com/spring-data-jpa-named-entity-graphs
https://thorben-janssen.com/jpa-21-entity-graph-part-1-named-entity/
https://thorben-janssen.com/hibernate-tip-entitygraph-multiple-subgraphs/
'java' 카테고리의 다른 글
Java TimeUnit (0) | 2024.01.30 |
---|---|
[Java]타임존 처리 ZonedDateTime + moment-timezone (0) | 2024.01.17 |
[JPA] Service Layer 에서 DataIntegrityViolationException 처리 (0) | 2024.01.10 |
[JPA] PK가 아닌 컬럼을 사용하여 연관관계 설정 (0) | 2024.01.03 |
점으로 구분된 버전 문자열 비교 (0) | 2023.12.06 |