[스프링 데이터 JPA] 스프링 데이터 JPA 분석

[1] 스프링 데이터 JPA 구현체 분석

스프링 데이터 JPA가 실제로 어떻게 동작하는지 알아보는 시간 🙃

스프링 데이터 JPA가 제공하는 공통 인터페이스의 구현체:
org.springframework.data.jpa.repository.support.SimpleJpaRepository

  • @Repository 적용: JPA 예외를 스프링이 추상화한 예외로 변환 (스프링빈의 컴포넌트 스캔 대상이 됨. 즉 읽혀 스프링 컨테이너에 등록된다는 것.)
  • @Transactional 트랜잭션 적용:
    - JPA의 모든 변경은 트랜잭션 안에서 동작 (delete, save... 모두)
    - 스프링 데이터 JPA는 변경(등록, 수정, 삭제) 메소드를 트랜잭션 처리 (항상 트랜잭션 안에서 처리하지 않으면 예외가 발생함)
    - 서비스 계층에서 트랜잭션을 시작하지 않으면 리포지토리에서 트랜잭션 시작
    - 서비스 계층에서 트랜잭션을 시작하면 리포지토리는 해당 트랜잭션을 전파 받아 사용
    - 그래서 스프링 데이터 JPA를 사용할 때 트랜잭션이 없어도 데이터 등록, 변경이 가능했음 (사실 트랜잭션이 리포지토리 계층에 걸려있는 것)
  • @Transactional(readOnly = true):
    - 데이터를 단순히 조회만하고 변경하지 않는 트랜잭션에서 readOnly = true 옵션을 사용하면 플러시를 생략해 약간의 성능 향상이 됨.
    (원래는 기본적으로 트랜잭션이 끝날 때 DB에 플러시하고 커밋함.
    플러시하지 않는다는 것은 DB에 변경감지를 하지 않고, 데이터를 보내지 않겠다. 즉, 더티체킹 과정을 생략하겠다는 것임.)

⭐️ save() 메소드

  • 새로운 엔티티면 저장(persist)
  • 새로운 엔티티가 아니라면 병합(merge)

병합은 DB의 데이터를 가져와 현재 내가 가지고 있는 데이터(파라미터로 넘긴 애)로 바꿔치기 하는 것이다.
그래서 되도록이면 merge를 쓰지 않고 변경 감지를 사용하여야 한다.

원래 merge는 어쩌다가 영속성 컨텍스트를 벗어난 경우 다시 영속성 컨텍스트에 넣어야 할 경우 사용하는 것이라고 한다. 아무튼 업데이트나 이런 건 변경 감지를 사용하자.


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

업데이트는 변경 감지, 저장은 persist를 사용하자.
merge는 사용할 일이 거의 없다고 보면 된다.

  • 새로운 엔티티를 판단하는 기본 전략
    - 식별자가 객체일 때 null로 판단 (😯 "pk 값이 null이면 새로운 값이네")
    - 식별자가 자바 기본 타입일 때 0으로 판단
    - Persistable 인터페이스를 구현해 판단 로직 변경 가능

Persistable


    public interface Persistable<ID> {
        ID getId();
        boolean isNew();
    }

참고로 JPA 식별자 생성 전략이 @GenerateValuesave() 호출 시점에 식별자가 없으므로 새로운 엔티티로 인식해 정상 동작한다.

그런데 JPA 식별자 생성 전략이 @Id만 사용해 직접 할당이면 이미 식별자 값이 있는 상태로 save()를 호출한다. 따라서 이 경우에는 merge()가 호출된다.

merge()는 우선 DB를 호출해 값을 확인하고, DB에 값이 없으면 새로운 엔티티로 인지하므로 매우 비효율적이다.
따라서 Persistable를 사용해 새로운 엔티티 확인 여부를 직접 구현하기는 효과적이다.

그리고 등록시간(@CreatedDate)를 조합해 사용하면 이 필드로 새로운 엔티티 여부를 편리하게 확인할 수 있다. (@CreatedDate값이 없으면 새로운 엔티티로 판단)


좋은 웹페이지 즐겨찾기