JPQL 패치 조인의 한계
① 패치 조인 대상에는 별칭을 줄 수 없다.
- 하이버네이트는 가능하나, 가급적 사용하지 않는다.
jpql = "select t from Team t join fetch t.members m"; // 패치 조인 대상에는 Alias를 주면 안된다.
패치 조인은 연관관계를 다 끌고 오는 건데, 별칭을 줘서 데이터를 누락시켜서 가져오는 가능성도 있다.
패치 조인에서 별칭을 쓰지 않아야 하는 이유는
→ 데이터의 정합성이나 객체 사상이 안맞을 가능성이 높기 때문에
쓴다면 다대일 관계로 만들고 쓰는게 좋다.
jpql = "select m from Member m join fetch m.team";
② 둘 이상의 컬렉션은 패치 조인을 사용할 수 없다. → 데이터 정합성 문제 때문에 (일대다대다)
③ 컬렉션을 패치 조인하면 페이징 API(setFirstResult, setMaxResults)를 사용할 수 없다.
- 일대일, 다대일 같은 단일 값 연관 필드들은 패치 조인해도 페이징은 가능하다.
- 하이버네이트는 경고 로그를 남기고 메모리에서 페이징을 처리한다. (매우 위험)
jpql = "select t from Team t join fetch t.members m";
List<Team> result = em.createQuery(jpql, Team.class)
.setFirstResult(0)
.setMaxResults(2)
.getResultList();
페이징 API를 적용하였지만, 실제로 나간 쿼리에는 페이징이 적용되지 않았다.
메모리 상에서만 적용되었다고 경고 로그를 확인할 수 있다.
페이징을 그래도 써야한다면
jpql = "select t from Team t"; // 즉시 로딩
List<Team> result = em.createQuery(jpql, Team.class)
.setFirstResult(0)
.setMaxResults(2)
.getResultList();
이렇게 되면 성능이 많이 떨어진다.
데이터가 많을수록 성능이 떨어진다.
이를 방지하기 위해서
Team.java
@BatchSize(size = 100)
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
한번에 팀A, 팀B에 들어가는 모든 내용을 다 가져오는 결과를 가져온다.
글로벌하게 배치사이즈를 설정해두는 방법도 있다.
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/jpaStudy2"/>
<!-- <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>-->
<property name="hibernate.dialect" value="dialect.MyH2Dialect"/>
<!--<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle12cDialect"/>-->
<!-- 옵션 -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<property name="hibernate.jdbc.batch_size" value="10"/>
<property name="hibernate.hbm2ddl.auto" value="create" />
<!-- 배치 사이즈 글로벌 세팅 -->
<property name="hibernate.default_batch_fetch_size" value="100"/>
</properties>
</persistence-unit>
</persistence>
<property name="hibernate.default_batch_fetch_size" value="100"/>
이렇게 property를 추가해주면 된다.
해당 클래스에서 컬렉션 형태로 데이터를 저장하는 필드에 @BatchSize 를 지정해주거나
글로벌하게 배치 패치 사이즈를 지정해주면 연관된 엔티티들을 SQL 한번에 조회할 수 있다.
이렇게 함으로써 성능 최적화를 도모할 수 있다
@OneToMany(fetch = FetchType.LAZY) // 글로벌 로딩 전략
패치 조인을 적용한 것은 글로벌 로딩 전략보다 우선한다.
실무에서 글로벌 로딩 전략은 모두 지연로딩이다.
그래서, 최적화가 필요한 곳은 패치 조인을 적용해야한다.
※ 정리
(1) 모든 것을 패치 조인으로 해결할 수는 없다.
(2) 패치 조인은 객체 그래프를 유지할 때 사용하면 효과적이다.
(3) 여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야 한다면,
패치 조인보다는 일반 조인을 사용하고 필요한 데이터들만 조회해서 DTO로 반환하는 것이 효과적이다.
'자바 > JPA' 카테고리의 다른 글
[JPA/Java] JPQL 엔티티 직접 사용 (0) | 2023.05.11 |
---|---|
[JPA/Java] JPQL 다형성 쿼리 - TYPE, TREAT (0) | 2023.05.11 |
[JPA/Java] JPQL fetch join (패치 조인) (2) | 2023.05.10 |
[JPA/Java] JPQL 경로 표현식 (0) | 2023.05.10 |
[JPA/Java] JPQL 기본함수 및 사용자 정의 함수 호출 (0) | 2023.05.10 |
댓글