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);
}