인프런 커뮤니티 질문&답변

김민지님의 프로필 이미지
김민지

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

페치 조인 2 - 한계

fetch join alias

작성

·

409

·

수정됨

2

fetch join 시 alias를 사용해서 필터링하는게 왜 안되는걸까요?

이거에 대한 답변으로 디비상태와 객체상태의 일관성이 깨지게 됨을 보통 얘기하시는것같아요

alias를 사용해서 필터링해버리면 실제 디비에 있는 데이터보다 적은 개수가 나오게 되니까요.

근데 어차피 그 필터링된 결과만을 결과로 리턴해주어야한다면

사용해도 괜찮을 것 같은데

디비상태와 객체상태의 일관성이 깨지는게 왜 문제가될까요?

그래서 생각을 해봤는데 크게 다음인것같아요

- 유지관리어려울 수 있음

- 캐싱문제

근데 저 두 문제가 정말 큰 문제가 되는지를 잘 모르겠어요...;

유지관리 어려울수야 있겠지만 그렇게 까지 어려울지도 잘 모르겠고, 캐싱문제(쿼리결과캐싱)도 저 코드에 의해 영향이 얼마나 많이 갈지..도 잘 모르겠어요

저 유지관리/캐싱 문제가 아니라.. 2차캐시때문인가요?

예를들어서
team과 member가 일대다 연관이고
team을 select해온다는 sql이 있다고 가정

1. fetch join + on 절 : 디비에 있는 일부 데이터 불러옴
2. fetch join 만있어서 디비에있는 모든 데이터 불러옴

하나의 트랜잭션에서 1호출 뒤에 2를 호출하면
디비에 쿼리를 날리긴하지만 이미 team_id에 해당하는 객체가 영속성 컨텍스트에있어서 가져온거버림
그래서 추후에 문제가생길 수 있음
--- 인건가요?

답변 2

3

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 김민지님

"fetch join"을 이용해 연관된 엔터티들을 조회할 때, 특히 JPA에서, alias를 이용한 필터링을 해버리면 몇 가지 문제가 발생할 수 있습니다. 주로 디비 상태와 객체 상태의 일관성에 관한 문제입니다.

먼저 간단히 개념적인 문제에 대해 알아보죠.

1. 일관성의 손실: JPA와 같은 ORM(Object-Relational Mapping) 프레임워크의 주 목표 중 하나는 데이터베이스와 객체 지향 프로그래밍 사이의 패러다임 간극을 극복하는 것입니다. 데이터베이스의 테이블과 클래스 간의 매핑을 제공하는 것 외에도, ORM은 객체의 상태와 데이터베이스의 상태 간의 일관성을 유지하는데 중요한 역할을 합니다. alias를 사용해서 필터링을 하게 되면, 조회된 객체 상태가 실제 데이터베이스 상태를 반영하지 않게 됩니다. 이로 인해 애플리케이션 내에서의 객체와 데이터베이스 간의 상태 불일치가 생길 수 있습니다.

2. 영속성 컨텍스트와 2차 캐시 문제: 영속성 컨텍스트는 현재 트랜잭션에서 사용되는 엔터티의 상태를 관리합니다. 특히 fetch join을 사용하면 연관된 엔터티들까지 한꺼번에 영속성 컨텍스트에 로드됩니다. 그런데 중간에 alias를 통한 필터링을 하면, 일부 엔터티만 영속성 컨텍스트에 로드되게 됩니다. 이 후 동일한 트랜잭션 내에서 다른 쿼리를 통해 동일한 엔터티를 조회하려고 하면, 이미 영속성 컨텍스트에 있는 엔터티 상태를 반환받게 되므로, 원치 않는 결과나 상태 불일치가 발생할 수 있습니다. 또한 2차 캐시는 여러 트랜잭션 간에 공유되는 캐시입니다. alias를 통한 필터링으로 인해 잘못된 상태의 엔터티가 2차 캐시에 저장될 수도 있습니다.

3. 유지 관리의 어려움: 코드의 복잡도가 증가하게 되면, 향후 코드를 이해하거나 수정하는 것이 더 어려워질 수 있습니다. 특히 alias를 사용한 필터링은 예상치 못한 사이드 이펙트를 유발할 수 있으므로 코드의 유지 관리 측면에서도 좋지 않습니다.

4. 캐싱 문제: JPA는 쿼리 결과 캐싱 뿐만 아니라 엔터티 캐싱도 지원합니다. 이 때 필터링된 결과가 캐시에 저장되면, 다른 곳에서는 전체 데이터를 기대하면서 해당 캐시를 조회할 수 있습니다. 이렇게 되면, 캐싱을 이용해 오히려 잘못된 데이터를 가져올 위험이 있습니다.

결국, fetch join에서 alias를 이용한 필터링은 일관성 문제, 캐싱 문제, 유지 관리의 어려움 등 다양한 문제를 유발할 수 있습니다. 따라서 일반적으로는 fetch join 시 alias를 사용한 필터링을 지양하는 것이 좋습니다.

하지만 여기에서 2차 캐시를 사용하지 않는다면 2차 캐싱에 관한 문제는 이슈가 되지 않습니다. 유지 관리의 어려움은 인지하고 사용한다고 가정할께요. 그렇다면 일관성의 손실 부분만 남게 됩니다.

일관성의 손실이라는 것이 어떻게 발생하는지 예를 들어드리겠습니다.

 

데이터 모델:

1. Author 엔터티가 있으며, 각각의 Author는 여러 권의 책을 쓸 수 있습니다.

2. Book 엔터티는 책의 정보를 담고 있으며, Author에게 연관됩니다.

데이터베이스 상의 데이터:

- Author: John (ID: 1)

- 작성한 책: Book A, Book B, Book C

이제 이 상태에서 애플리케이션을 통해 다음의 일련의 작업을 수행한다고 가정해봅시다.

작업 순서:

1. 첫 번째 쿼리: Author와 연관된 Book들 중 "Book A"만 조회합니다.

SELECT a FROM Author a JOIN FETCH a.books b WHERE b.name = 'Book A' AND a.id = 1

2. 이 쿼리의 결과로 영속성 컨텍스트에는 다음의 상태로 로드됩니다:

- Author: John (ID: 1)

- 작성한 책: Book A

3. 두 번째 작업: 동일한 트랜잭션에서, 애플리케이션 로직을 통해 Author ID 1에 연결된 모든 책을 가져오려고 합니다.

Author author = entityManager.find(Author.class, 1L);

List<Book> books = author.getBooks();

일반적인 상황이라면 books 리스트는 Book A, Book B, Book C를 모두 포함해야 합니다.

그러나 첫 번째 쿼리에서 영속성 컨텍스트에 "Book A"만 로드되었기 때문에, 영속성 컨텍스트는 이미 해당 정보를 가지고 있다고 판단하고 데이터베이스로부터 추가적인 책 정보를 가져오지 않습니다. 따라서 books 리스트는 "Book A"만 포함하게 됩니다.

문제점:

- 상태 불일치: 실제 데이터베이스 상의 정보와 애플리케이션 내부의 상태가 불일치합니다. 데이터베이스에는 세 권의 책이 있지만, 애플리케이션 내부에서는 한 권만 인식하고 있습니다.

- 비효율적인 작업: 추후에 Author와 관련된 모든 책을 다시 데이터베이스에서 가져오려 할 때, 영속성 컨텍스트는 이미 필요한 정보를 가지고 있다고 판단하고 데이터베이스에 추가적인 쿼리를 실행하지 않습니다. 이로 인해 애플리케이션은 잘못된 정보를 사용하게 됩니다.

- 버그 유발: 이런 상태 불일치로 인해 애플리케이션의 다른 부분에서 예상치 못한 동작이나 결과가 발생할 수 있습니다.

이러한 문제는 alias를 사용한 필터링과 영속성 컨텍스트의 동작 방식 때문에 발생하며, 이로 인해 데이터베이스 상태와 애플리케이션 내부 상태 간의 일관성이 손상되는 것입니다.

 

정리

fetch join 시 alias를 사용해서 필터링하는 것은 실무에서 필요합니다. 단 조회에서만 사용해야 하고, 데이터를 변경하는 작업이 있을 경우에는 사용하지 않는 것이 좋습니다.

감사합니다.

1

안녕하세요, 인프런 AI 인턴이에요.

fetch join을 사용할 때 alias를 사용해서 필터링하는 것은 기본적으로 지원되지 않습니다. 이는 JPA의 특성 중 하나로, fetch join은 연관된 엔티티를 함께 조회하기 위한 것이기 때문에 별도의 필터링이나 조건을 적용할 수는 없습니다.

alias를 사용해서 필터링을 하고자 한다면, 별도의 쿼리를 작성하여 필요한 데이터를 조회하는 방법이 있습니다. 이렇게 하면 원하는 조건에 맞는 데이터만을 가져올 수 있습니다.

fetch join을 사용하면 객체 상태와 데이터베이스 상태는 일치하지 않을 수 있습니다. 특히, 데이터베이스에는 존재하지 않는 객체가 메모리에 존재할 수 있습니다. 이는 실제 데이터와의 일관성을 무너뜨릴 수 있으며, 유지보수와 캐싱 등에 어려움을 초래할 수 있습니다.

결론적으로, fetch join은 연관된 엔티티를 함께 조회하기 위한 용도로 사용되는 것이라고 이해하시면 됩니다. 필터링된 결과를 가져오기 위해서는 별도의 쿼리를 작성하여야 합니다.

더 자세한 내용이나 예시 코드가 필요하시다면 추가 정보를 알려주세요. 감사합니다.

김민지님의 프로필 이미지
김민지

작성한 질문수

질문하기