Spring JPA 정리

영속성 전이

  • ALL : 모두 적용, 라이프 사이클이 같은 자식을 관리하는 엔티티가 부모 하나일때만 사용.
  • PERSIST : 영속
  • REMOVE : 삭제
  • MERGE : 병합
  • REFRESH : REFRESH
  • DETACH : DETACH

CascadeType.REMOVE 는 부모 엔티티를 삭제할 때 연관된 자식 엔티티를 삭제한다.

Parent.delete() : 부모와 자식 엔티티 삭제.

Parent.getChilds().remove() : 자식 엔티티 삭제.

Parent.getChilds().clear() : 자식 엔티티 삭제되지 않음.

orphanRemoval = true 는 연결이 끊어진 자식 엔티티를 삭제한다. (고아 객체)

@Modifying

@Query 어노테이션을 사용하여 쿼리를 직접 수행할 때 DML(INSERT, UPDATE, DELETE)를 사용하는 경우 붙여야 하는 어노테이션이며, @Modifying를 붙이지 않으면 오류가 발생한다.

@Modifying의 속성 값

clearAutomatically : clearAutomatically의 default 값은 false이며, 쿼리 수행 후 영속성 컨텍스트를 비울지 여부를 지정한다. clearAutomatically가 false이면 JPQL 쿼리가 실행된 후 영속성 엔티티는 쿼리 이전에 캐싱된 정보가 남이 있기 때문에 DB와 싱크가 맞지 않게 된다. clearAutomatically=true로 설정하면 캐싱된 정보를 지워서 다음 엔티티 조회 시 DB에서 갱신된 값을 가져올 수 있도록 한다.

flushAutomatically : flushAutomatically의 default 값은 false이며, 보통 JPQL 쿼리가 DB에 실행되기 전에 그 시점까지 사용된 영속성 엔티티는 flush된다.

다만, JPQL에 영향을 받는 영속성 엔티티만 flush되기 때문에 생각하지 못한 버그가 발생할 수도 있다.

flushAutomatically=true로 설정하면 JPQL이 실행되는 시점까지 사용된 모든 영속성 엔티티를 flush 된다.

다만 위와 같이 설정해야 되는 경우는 하나의 request(요청)에서 select, update, select와 같이 여러개의 작업이 수행될 때 의미가 있다. 그 이유는 영속성 컨텍스트는 http reuqest 마다 별개의 영속성 컨텍스트가 생성되기 때문이다.

@Modifying 에 clearAutomatically 가 필요한 경우

// controller
@PostMapping("/test")
public void testModifying(@RequestBody Long memberSeq) {
    this.testService.testModifying(memberSeq);
}

// testService
@Transactional
public void testModifying(Long memberSeq) {
    Member member = this.memberRepository.findById(memberSeq);
    
    this.memberRepository.updateName(memberSeq, "테스터");
    
    member = this.memberRepository.findById(memberSeq);
    
    this.logRepository.save(LogEntity.builder()
                       ....
                       .setMemberName(member.getName()) //clearAutomatically = true 이면 테스터, false이면 이전 값
                       .build()) 
}

// memberRepository
@Modifying (clearAutomatically = true, flushAutomatically = true)
@Query(value = " update Member m set m.name = :name " +
               " where m.memberSeq = :memberSeq")
void updateName(@Param("memberSeq") Long memberSeq,
                       @Param("name")  String name);

필요 없는 경우

post에서 clearAutomatically=false로 실행하여도 http request 마다 영속성 컨텍스트가 다르므로

이후 get 요청을하면 db에서 새로운 값(테스터)을 가져온다.

// controller
@GetMapping("/test")
public ResponseEntity<String> getTestModifying() {
    return ResponseEntity.ok()
            .body(this.testService.getTestModifying());
}

@PostMapping("/test")
public void testModifying(@RequestBody Long memberSeq) {
    this.testService.testModifying(memberSeq);
}