본문 바로가기
spring & boot/JPA

[JPA]JpaRepository 원리 및 내부 분석(EntityManager 자동주입, 상속관계)

by lucas_owner 2023. 8. 30.

JpaRepository 원리 및 내부 분석(EntityManager 자동주입, 상속관계)

1. 서론(찾아보게된 계기)

- 얼마전 QueryDsl 적용을 하던 도중, 기존에 Spring Data JPA 에서 사용하던 JpaRepository 를 extends 받는 방식과

QueryDsl 을 동시에 적용하던 도중, 대다수의 블로그나 예제에서는 QueryDsl 을 사용하는 Repository에 Entity Manaer를 적용하는 방식을 사용하고 있었다.

 

하지만 기존에 사용하던 JpaRepositoryEntityManager 를 자동주입을 해주어서 extends 를 받는것만으로도

EntityManager 를 별도로 설정하지 않고 사용 할 수 있었다. 

 

그래서 기존의 JpaRepositoryEntityManager 자동주입의 이점을 누리면서 QueryDsl 을 같이 사용 하는 방식을 고민하며, 

Spring Data Jpa 라이브러리의 JpaRepository 원리와 내부구조에 대해서 알아보게 되었다.

 

목차

  1. JpaRepository 의 상속관계 
    1. 각 interface의 기능 및 정의
  2. JpaRepository 의 상세 구조
  3. EntityManager 자동 주입 원리

 

1. JpaRepository 의 상속관계 

Spring Data Jpa 라이브러리 관계

위의 클래스 다이어그램은, JpaRepository 와 연관된 인터페이스, 클래스의 상속관계를 나타낸 것 이다. 

JPA 를 사용했던 사람이라면 일반적으로 Repository, CrudRepository, JpaRepository 셋중 하나는 사용해본 경험이 있을것이다.

 

아래로 내려올 수록 기능구현이 더 많다. 위에서 부터 내려올 수록 정의된 기능들을 extends 받기 때문이다.

 

1-1 Repository 

Repository

단편적인 예로, Repository (최상위) 인터페이스 에는 아무것도 정의가 되어있지 않다.

 

그래서 실제로 사용할 때도 Repository 를 extends 받아 사용한다면, 메서드를 추가로 정의 해줘야만 사용이 가능하다.

아래의 예제에서 확인 할 수 있다. 

 

Repository extends interface

- 일반적으로 JPA를 사용하듯 Repository를 extends 한 interface 를 서비스 클래스에서 접근을 한다면

아래와 같이 아무것도 사용 할 수가 없다.

service Class

 

Repository interface는 필수적으로 메서드를 작성해줘야 사용가능한것이다. 

Repository
Service Class


1-2. CrudRepository 

하지만 CrudRepository 의 경우, 기본인 메서드들이 정의가 되어있다 

그렇기 때문에 별도로 정의하지 않고, 기본 CRUD 를 사용할 수 있는것이다. 

CrudRepository


1-3. PagingAndSortingRepository

- 해당 interface 의 경우, CrudRepository 를 extends 받아 그 기능들을 사용하면서, 

페이징 기능 및 정렬 기능을 사용할수 있는 메서드가 정의되어있다. -> Pageable

PagingAndSortingRepository


1-4. JpaRepository 

- 위의 3개의 interface 를 extends 받은 JpaRepository 는 각 interface 의 메서드를 @Override 하거나 추가기능들이 정의가 되어있다. 

JpaRepository

위에서 extends 받은 interface 들의 기능들을 사용하면서, QueryByExampleExecutor 인터페이스 또한 extends 받으며,

JPA 를 쉽게 사용할 수 있게 되는 것 이다.

 


1-5 QueryByParamExector 란?

- 주어진 Entity 객체를 타겟으로 하여, 동적인 쿼리를 생성할 수 있게 도와주는 interface 이다. 

 

Page<T> findAll(Example<S> example, Pageable pageable) 이나, 

Iterable<T> findAll(Example<S> example, Sort sort) 와 같은 

특정 조건을 추가하여 조회하는 메서드가 추가되는것이다. 

 

*예시

// Optional<T> findOne(Example<S> example) 예시

// 특정 조건을 만족하는 사용자(User) 조회
Example<User> example = Example.of(new User("Kim", 25, "id"));
Optional<User> user = userRepository.findOne(example);

 

 

2. JpaRepository 의 상세 구조 

- 위의 인터페이스 설명하면서 간단한 구조 자체는 파악이 되었다. 

JpaRepository 는 extends PagingAndSortingRepositry<T, ID>, QueryByParamExecutor<T> 를 상속받는다.

 

여기서 JpaRepository 를 상속을 받는 interface 를 보자.


2-1 JpaRepositoryImplementation

- 해당 interface는 Spring Data Jpa 의 구현 메서드를 제공하는 인터페이스 이다. 

우리가 JPA 를 사용할때 모든 메서드를 직접 구현 하지 않고, 간단하게 사용하게 해주는 역할을 수행한다. 

 

1. CRUD 기본 메서드 지원.

2. 동적 쿼리 생성.

     - findByName: 메서드 선언시, Name 필드를 기준으로 동적 쿼리를 선언해준다.

3. 페이징, 정렬 지원.

4. QueryByParamExample 지원.

 

여기까지, 구조와 각각의 인터페이스의 기능과 역할에 대해 알아보았다,

이제 EntityManager 를 자동주입 해주는 클래스를 알아보도록 하자.

 

3. EntityManager 자동주입 원리

- 해당 파트가 마지막에 나온 이유는, 상속관계 구조도(사진) 에서, 최하위에 존재하는 Class 에서 이루어지기 때문이다.

JpaRepository 구현한 SimpleJpaRepository 에서 이루어진다. 

 

해당 클래스에서는, @Repository, @Transactional 과 같은 어노테이션이 붙어있는것으로 봐서 Data 접근계층에 해당하는것을 알 수 있다.

 

하지만, 개발자가 해당 클래스를 직접 사용 호출할일은 거의 없으며, Spring Data JPA 내부적으로 사용되며, JpaRepository 인터페이스의 기능들을 구현 처리한다. 

 

즉 SimpleJpaRepository 는 JpaRepository 의 구현체이다.

SimpleJpaRepository

우리가 직접 EntityManager 를 설정하고, 구현해주지 않아도 되는 이유가 바로 위 코드에 있다. 

 

JpaRepository 를 구현하는 클래스에서 -> EntityManager 를 주입하고 interface 에서 정의되었던, 기능들을 구현 사용할수 있게 해주기 때문이다. 

 

EntityManager 를 Repository 상에서 DI 하여 사용할때와 같은 메서드를 호출하는것을 확인할 수 있다.

em.persist();
em.merge();
em.createQuery();

 

하나의 예시로 save() 에 대한 JPA 전략이 해당 Class 에서 구현이 되어있는것을 볼수 있다. 

insert, update = save() 메서드로 처리 하는 방식을 뜻함.

 

JPA 라이브러리의 인터페이스나, 구현체와 같은것에 구조나, 예외처리등 단순하지만 복잡하고 정교하게 설계 되어있는것을 보고 아주 조금의 이해를 했더라도, 아키텍쳐 구조나 설계에 대한 것이 잘보이게 되는것 같다.

 

틀린점이나, 수정할부분이 존재한다면 댓글이나, 이메일 등등 알려주세요! 

 

 

*참고 문서

https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/support/SimpleJpaRepository.html

반응형

댓글