강의 수강 중 궁금했던 Update 쿼리에 대해 배운다.
- DirtyChecking + WriteBehind 에 의한 Persistent 상태의 Entity 에 변화가 일어났으며, find()를 호출해야하는 상황이 오면 Update 문이 자동으로 실행
- save() 호출 시 Entity 상태에 따른 EntityManager 의 persist() , merge() 사용으로 인한 Persistent 상태 전환 에서 사용되는 Update 문
- find, count, delete 메서드는 제공하는데 update는 열심히 찾아보아도 없더라.
사실 제공기능을 공부하다보니 필요할까 라는 생각도 들었었다.
메서드로 제공되지 않는 이유
- Persistent 상태의 Entity 변화를 감지(DirtyChecking) 후 변경이 있다면 변경(WriteBehind)
- 데이터 베이스에 동기화시키는 것을 Flush 라고 하며 이때 Update 문이 실행된다.
직접 정의해서 쓰고 싶다면
- @Modifying + @Query 로 직접 기입
- 추천되지 않는 방법이다.
public interface CommentRepository extends JpaRepository<Comment,Long> {
@Query("SELECT c, c.title as cTitle FROM #{#entityName} as c WHERE c.title = :title")
List<Comment> findByTitle(@Param("title") String title, Sort sort);
@Modifying
@Query("UPDATE Comment AS C SET C.title = ?1 WHERE C.id = ?2")
int updateTitle(String updateWord, Long id);
}
@Test
public void testUpdate(){
Comment comment = new Comment();
comment.setTitle("Spring?");
comment.setComment("Data JPA");
Comment savedComment = commentRepository.save(comment);
int updatedRow = commentRepository.updateTitle("Hibernate :)", 1L);
assertThat(updatedRow).isEqualTo(1);
List<Comment> selectedComment = commentRepository.findByTitle("Hibernate :)", Sort.by(Sort.Direction.ASC,"title"));
selectedComment.forEach(System.out::println);
assertThat(selectedComment.get(0).getId()).isEqualTo(1L);
assertThat(selectedComment.get(0).getTitle()).isEqualTo("Hibernate :)");
}
다시 find() 를 호출해도 변경된 레코드를 가져오지 않는다.
- 1차 캐싱기능 때문
- 하나의 트랜잭션으로 진행상태로 인해 Entity 객체는 여전히 Persistent 상태
- Persistent 상태의 Entity 를 find() 해오려고 했으니 PersistentContext 는 캐싱하고 있는 Entity 를 꺼냄
org.opentest4j.AssertionFailedError:
Expecting:
<"Spring?">
to be equal to:
<"Hibernate :)">
but was not.
Expected :"Hibernate :)"
Actual :"Spring?"
쿼리 실행시 PersistentContext 안의 Cache 를 Clear 해주어야 find() 호출시 DB에 접근해서 레코드를 가져올 수 있다.
- @Modifying 어노테이션의 clearAutomatically 속성을 true로 주면 메서드 실행 후 PersistentContext 에 Cache 된 데이터를 비운다.
- @Modifying 어노테이션의 flushAutomatically 속성을 true 주면 메서드 실행 전에 PersistentContext 에 Cache 된 데이터를 DB에 동기화 시킴
public interface CommentRepository extends JpaRepository<Comment,Long> {
@Query("SELECT c, c.title as cTitle FROM #{#entityName} as c WHERE c.title = :title")
List<Comment> findByTitle(@Param("title") String title, Sort sort);
@Modifying(clearAutomatically = true)
@Query("UPDATE Comment AS C SET C.title = ?1 WHERE C.id = ?2")
int updateTitle(String updateWord, Long id);
}
///
Hibernate:
select
comment0_.id as col_0_0_,
comment0_.title as col_1_0_,
comment0_.id as id1_0_,
comment0_.comment as comment2_0_,
comment0_.title as title3_0_
from
comment comment0_
where
comment0_.title=?
order by
comment0_.title asc
2020-11-18 22:53:26.327 TRACE 12304 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [Hibernate :)]
2020-11-18 22:53:26.331 TRACE 12304 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_0_] : [BIGINT]) - [1]
2020-11-18 22:53:26.335 TRACE 12304 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([comment2_0_] : [VARCHAR]) - [Data JPA]
2020-11-18 22:53:26.335 TRACE 12304 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([title3_0_] : [VARCHAR]) - [Hibernate :)]
2020-11-18 22:53:26.336 TRACE 12304 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_0_0_] : [BIGINT]) - [1]
2020-11-18 22:53:26.342 TRACE 12304 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_1_0_] : [VARCHAR]) - [Hibernate :)]
Comment{id=1, title='Hibernate :)', comment='Data JPA'}
DirtyChecking + WriteBehind 기능을 사용한 테스트 코드는 이러하다
@Test
public void testUpdate1(){
Comment comment = new Comment();
comment.setTitle("Spring?");
comment.setComment("Data JPA");
Comment savedComment = commentRepository.save(comment);
comment.setTitle("Hibernate :)"); // DirtyChecking + WriteBehind
List<Comment> selectedComment = commentRepository.findByTitle("Hibernate :)", Sort.by(Sort.Direction.ASC, "title"));
selectedComment.forEach(System.out::println);
assertThat(selectedComment.get(0).getId()).isEqualTo(1L);
assertThat(selectedComment.get(0).getTitle()).isEqualTo("Hibernate :)");
}
///
Hibernate:
select
comment0_.id as col_0_0_,
comment0_.title as col_1_0_,
comment0_.id as id1_0_,
comment0_.comment as comment2_0_,
comment0_.title as title3_0_
from
comment comment0_
where
comment0_.title=?
order by
comment0_.title asc
2020-11-18 22:59:01.282 TRACE 16880 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [Hibernate :)]
2020-11-18 22:59:01.288 TRACE 16880 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_0_] : [BIGINT]) - [1]
2020-11-18 22:59:01.289 TRACE 16880 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_0_0_] : [BIGINT]) - [1]
2020-11-18 22:59:01.305 TRACE 16880 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_1_0_] : [VARCHAR]) - [Hibernate :)]
Comment{id=1, title='Hibernate :)', comment='Data JPA'}
'springframework > Spring Data JPA' 카테고리의 다른 글
Spring Data JPA : Projection (0) | 2020.11.19 |
---|---|
Spring Data JPA : Entity Graph (0) | 2020.11.19 |
Spring Data JPA : Named Parameter , SpEL (0) | 2020.11.18 |
Spring Data JPA : Sort 시 내부함수 호출 (0) | 2020.11.18 |
Spring Data JPA : 쿼리 메서드 (0) | 2020.11.18 |