[Spring boot] JPA - Spring data JPA가 아닌 순수 JPA

  • 순수 JPA를 공부하면서 프로젝트 할 때 사용을 많이 했던 Spring Data JPA에서 순수 JPA가 어디서 돌아가는지 궁금하게 되었다.
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface FoldersRepository extends JpaRepository<Folders, Long>{
    List<Folders> findAllByUserId(Long userId);
}

위 코드는 스프링 data jpa를 사용한 코드이다. 인터페이스인 JPARepository를 따라가면 다음과 같은 형태가 나오게 된다.

 

JPA Repository

각 인터페이스들이 무엇을 하는지는 아직 파악을 못했지만 순수 JPA 코드는 SimpleJpaRepository에서 진행된다.

 

  • 순수 JPA에서 createEntityManagerFactory, createEntityManger, close, 트랜잭션.begin(), 트랜잭션.commit() 같은 걸 진행하게 된다.

 

SimpleJpaRepository에서 위를 다 하는 지 조금 더 파헤쳐보자

 

밑은 SimpleJpaRepository 코드이다.(부분만 가져왔다.)

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

	private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null";
	private static final String IDS_MUST_NOT_BE_NULL = "Ids must not be null";
	private static final String ENTITY_MUST_NOT_BE_NULL = "Entity must not be null";
	private static final String ENTITIES_MUST_NOT_BE_NULL = "Entities must not be null";
	private static final String EXAMPLE_MUST_NOT_BE_NULL = "Example must not be null";
	private static final String SPECIFICATION_MUST_NOT_BE_NULL = "Specification must not be null";
	private static final String QUERY_FUNCTION_MUST_NOT_BE_NULL = "Query function must not be null";

...

 

SimpleJpaRepository가 하는 일 분석

EntityManagerFactory 불러오기 (createEntityManagerFactory)

  • Springboot가 애플리케이션 시작할 때 이걸 함.
  • JPA를 순수하게 쓰면 persistence.createEntityMangerFactory(..)를 호출하지만, Spring boot에선LocalContainerEntityManagerFactoryBean 같은 설정을 통해 EMF를 Bean으로 만든다.

SimpleJpaRepository가 EMF를 만들지 않는다.

EntityManager 사용(createEntityManger, close)

  • Spring(정확히는 Spring ORM/ JPA 인프라)가 한다.
  • 일반적으로는 트랜잭션 경계에 맞춰 “현재 스레드에 바인딩된 “EntityManager”를 제공
  • 주입받는 EntityManger도 사실 프록시고, 실제 EM은 스프링이 열고 닫는다.

SimpleJpaRepository는 EntityManager를 new/create 하지 않고, 주입받아 사용만 한다.

트랜잭션 begin/commit/rollback 하기 전 준비

  • 이건 @Transactional + PlatformTransactionManager(JpaTransactionManager) 가 한다.
  • 트랜잭션 시작할 때:
    • EM을 준비(필요하면 생성)
    • EntityTransaction.begin()(또는 구현체 세션 시작)을 내부적으로 수행
  • 정상 종료하면 commit
  • 예외면 rollback

SimpleJpaRepository가 begin/commit/rollback을 직접 호출하지 않는다.

 

그러면 SimpleJpaRepository는 정확히 무엇을 하냐?

SimpleJpaRepository는 “레포지토리 메서드 구현체”로서 다음과 같은 것만 한다.

  • em.persist(entity)
  • em.merge(entity)
  • em.find(...)
  • em.remove(...)
  • em.flush()
  • em.createQuery(...) / Criteria 사용

즉, 비즈니스/트랜잭션 제어가 아니라 영속성 작업(persistence operations)을 호출하는 역할

 

 

이렇게 순수 JPA를 나눈이뉴는 Spring Data JPA는 Repository 구현체이고 트랜잭션/자원 관리는 “인프라 관심사”라서 분리해둔 것임.

 

그래서 코드가 깔끔해짐

@Transactional
public void serviceLogic() {
    repository.save(entity); // repository는 persist만 호출
} // 트랜잭션 종료 지점에서 스프링이 commit/rollback + close 처리

 

결론

Spring Data JPA에서 실행되는 순수 JPA 코드는 SimpleJpaRepository와 그 하위 쿼리 실행 계층에 존재하며 EntityManager의 생성 및 종료와 트랜잭션 제어는 Repository가 아닌 Spring JPA 인프라 계층에서 담당한다는 것을 확인했다. 즉, Spring Data JPA는 순수 JPA를 대체하는 것이 아니라, 순수 JPA의 사용 위치와 책임을 명확히 분리한 추상화 계층이다.