Web/JPA

[Data JPA] EP 5. 스프링 데이터 JPA 분석

목차 (클릭시 해당 목차로 이동)


     

     

     

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

     

    스프링 데이터 JPA 구현체 찾기

     

    JpaRepository의 구현클래스 중에서 SimpleJpaRepository가 스프링 데이터 JPA 구현체 입니다.

     

    SimpleJpaRepository를 들어가보면 @Repository, @Transactional(readOnly = true)가 걸려있습니다.

     

     

    특징

     

    1. jpa의 exception을 spring의 exception으로 매핑해 바꿔줍니다. (서비스계층으로 exception이 바껴서 올라감)

    그래서 하부 구현기술(예를들어 jpa->jdbc)을 바꿔도 기존 비즈니스 로직에 영향을 주지 않습니다.

     

    2. 구현체의 메소드에서 @Transaction이 걸려있기 때문에 스프링 데이터 JPA를 Transaction없이 써도 됩니다.

     

    save의 경우 @Transactional이 걸려있습니다.

     

    SimpleJpaRepository의 class 레벨에 @Transactional(readOnly = true) 가 걸려있기 때문에 데이터변경이 필요한 메서드에는 @Transactional이 걸려있습니다.

    +

    @Transaction(readOnly = true) 는 트랜잭션이 끝날때 하는 플러시를 생략해서 약간의 성능개선을 합니다.

     

     

    3. save()

    위의 save 코드를 보면 데이터가 이미 존재할경우 merge를 하는 것을 볼 수 있습니다.

    • merge: db에서 엔티티를 가져오고 새로운 엔티티로 교체합니다.
      merge의 단점 : db엔티티를 가져올때 select 쿼리가 나갑니다.

    그래서 가급적이면 데이터변경때 merge를 하지 말아야하고 변경감지기능으로 해야합니다.

     

     

     

     

     

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

     

    스프링 데이터 JPA의 구현체중에서 save를 다시 한번 살펴보겠습니다.

     

     

    만약 엔티티의 id 전략이 @GeneratedValue라면 isNew 검사 부분에서는 엔티티의 id가 null 되어 em.persist()를 호출하게 됩니다.

     

    그래서 em.persist() 후에 id가 Generated 됩니다.

     

     

     

    그런데 여기서 id전략이 GeneratedValue가 아니라 직접 값을 넣어주는 형태라면 isNew검사시 False가 나와서 em.merge를 호출하게 됩니다.

     

    엔티티를 저장할땐 persist, 변경할땐 변경감지기능을 써야합니다. 실제로 merge를 쓰는 경우는 거의 없습니다.

     

     

     

    그렇다면 id를 임의로 생성해 넣어야하는 경우에는 어떻게 해야할까요?

     

    만약 id를 임의로 생성해 넣어야 할때는 Persistable을 상속받아 isNew를 override해야합니다.

    그리고 createdDate를 이용해 새로운 엔티티인지 검사하면 됩니다.

     

    @CreatedDate를 사용하기 위해 @EntityListeners(AuditingEntityListener.class)를 해주었습니다.

     

    실제로는 BaseEntity를 이용하면 됩니다.

    package study.datajpa.entity;
    
    import lombok.AccessLevel;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import org.springframework.data.annotation.CreatedDate;
    import org.springframework.data.domain.Persistable;
    import org.springframework.data.jpa.domain.support.AuditingEntityListener;
    
    import javax.persistence.Entity;
    import javax.persistence.EntityListeners;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import java.time.LocalDateTime;
    
    @Entity
    @EntityListeners(AuditingEntityListener.class)
    @Getter
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class Item implements Persistable<String> {
    
        @Id
        private String id;
    
        @CreatedDate
        private LocalDateTime createdDate;
    
        public Item(String id) {
            this.id = id;
        }
    
        @Override
        public boolean isNew() {
            return createdDate == null;
        }
    }
    

     

     

     

     

     

    id 값이 이미 들어가 있지만

     

     

     

    isNew함수를 override해서 createdDate로 새로운 엔티티를 구별해냈기 때문에 em.persist가 호출된 것을 볼 수 있습니다.