Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

SpringDataJpa의 구현체 본문

인프런/[인프런] Spring Data JPA

SpringDataJpa의 구현체

Seung__ 2021. 8. 31. 22:05

1. SimpleJpaRepository


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

	@Transactional
	public <S extends T> S save(S entity) {
    
      if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
      } else {
      return em.merge(entity);
      }
  }
  ...
}
  • SpringDataJpa의 구현체
  • @Repository
    • 스프링 빈으로 등록
    • JDBC/JPA에서 예외가 터지면 Spring에서 사용할 수 있는 예외로 바꿔줌
    • 하부 기술을 바꿔도 예외 처리 메커니즘이 동일하게 유지됨.
  • @Transaction(readOnly = true)
    • SpringDataJpa의 모든 기능은 @Transactional이 걸린 상태에서 진행됨.
    • JPA의 모든 변경은 트랜잭션 안에서 동작
    • 스프링 데이터 JPA는 변경(등록, 수정, 삭제) 메서드를 트랜잭션 처리
    • 서비스 계층에서 트랜잭션을 시작하지 않으면 리파지토리에서 트랜잭션 시작
    • 서비스 계층에서 트랜잭션을 시작하면 리파지토리는 해당 트랜잭션을 전파 받아서 사용
    • 그래서 스프링 데이터 JPA를 사용할 때 트랜잭션이 없어도 데이터 등록, 변경이 가능했음
      (사실은 트랜잭션이 리포지토리 계층에 걸려있는 것임)
    • 참고로 ReadOnly=true로 지정하면
    • flush를 안해서 약간의 성능 향상을 얻을 수 있당

 

 

2. 중요


  • save() 메서드*
  • 새로운 엔티티면 저장( persist )
  • 새로운 엔티티가 아니면 병합( merge ) : 준 영속상태 -> 영속상태
  • 가급적 merge를 쓰지 말고 변경감지(dirty checking)을 활용하자.
 

💥변경 감지와 병합(merge)💥

너~~~~~~~~~~~무 중요 1. 변경감지, dirty checking 일반적으로 Transaction안에서 엔티티가 변경이 되면, flush가 되는 시점에 dirty checking을 통해 변경된 내용에 대해 update query가 실행된다. 이를 dirty c..

bsh-developer.tistory.com

 

 

 

 

3. 새로운 엔티티를 구분하는 방법


  • SimpleJpaRepository
    • 새로운 엔티티인지 판단하는 시점은 save()메서드 호출 시점이다.
    • 여기서 새로운 엔티티로 판단되면 persit, 아니면 merge
@Transactional
	@Override
	public <S extends T> S save(S entity) {

		Assert.notNull(entity, "Entity must not be null.");

		if (entityInformation.isNew(entity)) {
			em.persist(entity);
			return entity;
		} else {
			return em.merge(entity);
		}
	}
  • 새로운 엔티티를 판단하는 전략
    • 식별자가 객체일 때 null 로 판단-> pk가 null이면 새거! 
      pk에 값이 들어가는 시점은 persist 시점.
    • 식별자가 자바 기본 타입(primitive)일 때 0 으로 판단
      long은 null이 불가능. Long은 null 가능.
  • 문제점: @GeneratedValue를 사용하지 않고 직접 pk를 지정하는 경우
    • pk에 값이 있기 때문에 새로운 엔티티로 판단이 안됨
    • 따라서 save()호출하면 persist가 아니라 merge가 호출됨
    • merge는 DB에서 pk 로 엔티티를 찾은 다음 덮어쓰기함.
    • 근데 해당 엔티티는 새로운 엔티티라 DB에 없는데?
    • 잉? DB에 없으니 새거구나.. 이 때 insert 쿼리 날라감.
    • 어휴 비효율적임.
  • Persistable 인터페이스를 구현해서 판단 로직 변경 가능
    • Persistable(idType)을 구현해주자.
    • isNewI() 메서드를 구현하자.
    • @CreatedDate는 persist 직전에 호출되기 때문에, createdDate의 존재 여부를 기준으로 
    • 새로운 엔티티인지 판단이 가능함.
@Entity
@Getter
@EntityListeners(AuditingEntityListener.class)
public class Item implements Persistable<String> {

    @Id
    private String id;

    /**
     * persist 전에 호출됨.@PrePersist
     */
    @CreatedDate
    private LocalDateTime createdDate;

    protected Item() {
    }

    public Item(String id) {
        this.id = id;
    }

    @Override
    public boolean isNew() {
        return createdDate == null;
    }
}

 

 

 

4. 정리


  • JPA 식별자 생성 전략이 @GenerateValue 면 save() 호출 시점에 식별자가 없으므로
  • 새로운엔티티로 인식해서 정상 동작한다. 
  • 그런데 JPA 식별자 생성 전략이 @Id 만 사용해서 직접 할당이면 
  • 이미 식별자 값이 있는 상태로 save() 를 호출한다. 
  • 따라서 이 경우 merge() 가 호출된다. merge() 는 우선 DB를 호출해서 값을 확인하고, 
  • DB에 값이 없으면 새로운 엔티티로 인지하므로 매우 비효율 적이다. 
  • 따라서 Persistable 를 사용해서 새로운 엔티티 확인 여부를 직접 구현하게는 효과적이다.
  • 참고로 등록시간( @CreatedDate )을 조합해서 사용하면
  • 이 필드로 새로운 엔티티 여부를 편리하게 확인할수 있다. 
  • (@CreatedDate에 값이 없으면 새로운 엔티티로 판단)

 

 

5. GitHub : 210831 is NEW?


 

GitHub - bsh6463/SpringDataJpa

Contribute to bsh6463/SpringDataJpa development by creating an account on GitHub.

github.com

 

'인프런 > [인프런] Spring Data JPA' 카테고리의 다른 글

Projections  (0) 2021.08.31
Query By Example  (0) 2021.08.31
Web 확장 : 도메인 클래스 컨버터, 페이징 & 정렬  (0) 2021.08.31
Auditing  (0) 2021.08.30
사용자 정의 Repository  (0) 2021.08.30
Comments