2장. JPA
JPA의 구현체인 Hibernate의 엔티티매니저는 자체적으로 CRUD 메소드를 제공합니다. EntityManager의 find 메소드를 이용하면 키 값으로 하나의 로우를 조회할 수 있는데 이 메소드는 데이터를 모델객체에 담아서 돌려줍니다.
모델객체와 관계를 맺고 있는 다른 모델객체들의 데이터는 모델객체들의 연관관계에 따라 EAGER 또는 LAZY 로딩 정책이 적용됩니다.
예를 들어 부서(Dept)는 많은 직원(Emp)을 갖고 있고 직원은 한 부서에 속한다고 하면 두 개의 객체의 연관관계는 1:N이 됩니다. 부서 객체를 대상으로 질의하면서 부서 객체와 연관관계에 있는 직원객체도 같이 데이터를 구할 것인지 판단해야 하는데 N의 연관관계에 해당하는 경우 기본적으로 LAZY 로딩정책을 적용합니다. 그 반대로 직원 객체를 대상으로 질의하는 경우 직원 객체와 연관관계에 있는 부서객체도 같이 데이터를 구할 것인지 결정해야 하는데 1의 연관관계에 해당하는 경우 기본적으로 EAGER 로딩적책을 적용합니다.
EAGER 로딩정책인 경우 대상 모델객체의 데이터를 구할 때 연관된 모델객체들의 데이터도 같이 구해 옵니다. LAZY 로딩인 경우에는 대상 모델객체의 데이터만 구합니다. 추후에 대상 객체의 객체 그래프 탐색을 사용하는 시점에서 실제 데이터 대신 갖고 있는 프록시객체를 호출하여 그 시점에서 연관된 모델 개체의 데이터를 구해 옵니다. EAGER 로딩은 개발자 입장에서는 편리한 기능이지만 사용하지 않는 데이터를 구하기 위한 질의를 매번 수행하는 것은 낭비가 되므로 삼가야 합니다.
질의의 과도한 사용을 줄이기 위해서 JPA 기술을 사용할 때 엔티티매니저가 쿼리를 어느 시점에 얼마나 사용하는지, 사용하지도 않는 데이터를 구하기 위해 여러 쿼리가 사용되고 있지는 않는지 살펴 볼 필요가 있습니다. 바로 이 부분이 JPA를 사용하는 개발자가 갖춰야 할 핵심역량이라고 할 수 있습니다.
기본 로딩정책은 LAZY로 설정하고 필요할 때 점진적으로 EAGER 로딩정책을 적용해 가는 방식으로 관리하는 것이 좋습니다.
2.1. EntityManager
엔티티매니저가 제공하는 메소드를 이용하여 데이터를 구하는 코드를 살펴 보겠습니다.
#1 설명
첫 번째 파라미터 자리에는 엔티티 클래스를 지정합니다. 두 번째 파라미터 자리에는 고유한 키 값을 지정합니다. 1L "Long" 자료형을 사용한 이유는 엔티티 클래스 내에 존재하는 키의 역할을 수행하는 필드변수의 자료형을 따른 것입니다.
엔티티매니저는 @Entity 어노테이션이 붙어 있는 모델 클래스를 애플리케이션이 기동할 때 파악합니다. 따라서 파라미터로 엔티티 클래스를 주면 엔티티매니저는 어느 테이블을 사용해야 하는지 알고 있습니다.
만약 앞에서 같은 요청을 한 번이라도 이미 했다면 엔티티매니저는 Persistence Context를 통해 해당 모델객체를 이미 갖고 있는 상태이므로 갖고 있는 객체를 바로 돌려 줄 것입니다. Persistence Context는 데이터베이스에 질의하여 얻어 온 데이터를 보관하는 모델 객체를 원본과 복사본으로 관리하며 개발자에게 돌려 주는 객체는 복사본 객체입니다. 원본을 따로 보관하는 것은 트랜잭션이 끝날 때 데이터의 변화를 체크하여 이를 반영하기 위한 행동입니다. 원본 객체를 보관했다가 같은 데이터를 요청 받으면 갖고 있는 객체를 바로 주는 기능을 1차 캐시 기능이라고 부릅니다.
#2 설명
직원이 여러 부서에 배치되는 것이 아니라 부서가 여러 직원을 갖고 있는 것이 일반적인 모습이므로 Dept와 Emp의 관계는 1:N이 됩니다. 위 코드 중 #1에 해당하는 코드는 키에 맞는 부서 데이터를 요청하고 있습니다. 이 경우 N에 해당하는 디폴트 로딩정책은 LAZY 이므로 dept 변수가 가리키는 객체는 dept 테이블 데이터만 있고 직원객체와 연관된 데이터는 없는 상태입니다. 이러한 로딩정책을 기본적으로 사용하는 이유는 N에 해당하는 테이블의 로우의 개수가 수백만 건이 넘을 정도로 매우 클 수도 있기 때문에 요청할 때 마다 연관 테이블의 데이터를 가져오는 행동방식은 불합리하기 때문입니다.
dept 변수가 가리키는 객체 내에 emps 필드변수는 값을 갖고 있는 상태가 아니라 값을 구할 수 있는 프록시 객체를 대신 가리키고 있습니다. 개발자가 데이터를 사용하기 위해서 요청하는 시점에 프록시 객체를 기동하여 데이터를 구해 옵니다. dept.getEmps 메소드를 호출하는 시점에 이렇게 처리됩니다.
일부 데이터만 사용하면 되는데 전체 데이터를 메모리에 올려두고 사용하는 것은 메모리관리 측면에서 보면 매우 비효율적입니다. 그러므로 개발자는 쿼리를 적절히 조절해서 필요한 시점에 필요한 만큼의 데이터를 구해서 사용해야 할 것입니다.
JPA를 사용하면 개발자는 엔티티매니저가 관리하는 객체를 대상으로 데이터를 요구하게 됩니다. 데이터를 엔티티매니저에게 요구하기 위해서 SQL과 거의 비슷한 JPQL을 사용합니다. 개발자가 데이터베이스에 직접 질의하는 것이 아니라 엔티티매니저의 엔티티 객체를 대상으로 질의하기 때문에 SQL 대신 JPQL을 사용하는 것이며 이를 객체지향 쿼리라고 부릅니다.
댓글 없음:
댓글 쓰기