Spring/Spring Data JPA

Spring Data JPA 분석

민철킹 2021. 6. 16. 19:07

Spring Data JPA 구현체 분석

공통 인터페이스(JpaRepository)의 구현체

==> org.sprigframework.data.jap.repository.support.SimpleJpaRepository

SimpleJpaRepository 클래스 내에 CRUD를 하는 여러 메서드(save, findAll....)등을 구현해놓았다.

SimpleJpaRepository는 @Repository 애노테이션이 붙어있는데 여기에는 두가지 의미가 있다.

 

1. 컴포넌트 스캔의 대상이 되어 스프링이 읽어 컨테이너에 스프링 빈으로 등록

 

2. JPA(JDBC도 마찬가지)에서 발생한 예외와 같은 영속성 계층의 예외들을 Spring 예외로 바꿔준다.

  • 서비스 계층이나 컨트롤러 계층에 예외를 넘길 때 JPA나 JDBC 예외가 아닌 Spring 예외로 넘김
  • 하부 기술을 JDBC에서 JPA로 바꿔도 예외를 처리하는 매커니즘은 동일하다는 이점이 존재
  • 즉, 기존 비즈니스 로직에 영향을 주지 않는다.

 

또한 @Transactional(readOnly = true)로 되어 있다.

==> Spring Data JPA의 모든 기능은 일단 트랜잭션을 걸고 시작한다는 의미이다. 

==> Spring Data JPA는 리포지토리 계층에서 트랜잭션을 시작해버림(사용하는 리포지토리에 트랜잭션이 안 걸려있더라도)

==> 클래스 레벨에서 readOnly를 true설정한 것이고 save나 delete같은 특정 메서드레벨에 따로 @Transational을 사용

==> 서비스 계층에서 트랜잭션을 시작하면 그것을 이어받아 리포지토리에서도 사용

==> 서비스 계층에서 트랜잭션을 시작하지 않으면 리포지토리에서 트랜잭션 시작

==> "readOnly=true" 옵션을 사용하면 트랜잭션이 끝날 때 flush()를 생략함.

따라서 Spring Data JPA를 사용할 때 우리는 트랜잭션을 걸지도 않았는데 memberRepository.save() 와 같은 트랜잭션 내에서 일어나야했을 행위들이 가능했던 것이다.

참고로, save가 끝나고 나오는 순간 트랜잭션이 끝나 영속성 컨텍스트가 없어져버린다.

위의 save 메서드를 보면 isNew를 통해 새로운 엔티티라면 persist, 그렇지 않다면 merge를 실행하는데, 새로운 엔티티인것을 어떻게 판단하는 것일까?

 

 

 


새로운 엔티티를 구별하는 방법

 

새로운 엔티티를 판단하는 기본 전략

  • 식별자(@Id)가 객체일 때 null이면 새로운 엔티티로 판단
  • 식별자가 자바 기본 타입일 때 0이면 새로운 엔티티로 판단
  • Persistable 인터페이스를 구현해서 판단 로직 변경 가능

 

이러한 Item 엔티티가 존재하고 JpaRepository를 상속받는 ItemRepository가 존재한다고 가정해보자.

컨트롤러나 서비스 계층에서 itemRepository.save(item)을 호출하였다고 가정해보자.(현재 저장된 아이템은 없음)

@GenerateValue는 persist()를 하면 그 때 값이 생성된다. 즉, 예시 상황과 같이 저장된 아이템이 하나도 없는 경우에는 id의 값이 null이다.(Long타입이니까) 따라서 스프링 데이터 JPA가 새로운 엔티티로 판단하고 persist를 수행한다.

 

만약 저장된 아이템이 여러개 존재하여 id의 값이 null이 아니었다면 새로운 엔티티가 아니므로 merge를 수행했을 것이다.

@GeneratedValue를 사용하지 않고 생성자를 통해 아이디 값을 생성한 후 save를 한다면?(id가 String 타입)

Item item = new Item("a");
itemRepository.save(item);

save를 실행하게되면 isNew 메서드가 실행되는데 현재 id값이 null이 아니므로 else로 빠져 merge가 실행된다.

merge의 동작 과정을 이야기해보면 merge는 우선 db에 해당 값이 존재하는 것을 가정하고 동작한다. 따라서 위와 같은 상황에서는 id='a'로 db로 select문이 실행된다. 만약 조회되는 것이 없으면 새로운 엔티티임을 판단하고 insert한다. 만약 조회되는 것이 있으면 merge를 실행한다.(원래 것에 현재 것을 뒤집어씌워버림 update)

 

 

판단 전략을 변경하고 싶다면 Persistable 인터페이스를 구현하도록 한다.

isNew 메서드의 로직을 작성해주면 된다.

앞서 배웠던 @CreatedDate를 통해 생성일이 null이면 새로운 엔티티로 판단하는 isNew메서드를 새로 구현하였다.

반응형

'Spring > Spring Data JPA' 카테고리의 다른 글

나머지 기능들  (0) 2021.06.17
확장 기능  (0) 2021.06.15
JPA Hint, Lock  (0) 2021.06.14
@EntityGraph  (0) 2021.06.14
벌크성 수정 쿼리  (0) 2021.06.14