flush()
가 자동으로 호출된다.flush()
가 자동으로 호출되지 않는다.@Modifying
을 사용해야만 하고, 1, 2번과 동일하게 작동한다.@Modifying(flushAutomatically = true)
옵션을 통해 flush()
자동 호출이 가능하다)JPA에서 flush()
자동 호출은 언제 일어날까? 내가 알고 있던 것은 다음과 같은 세 가지 상황이었다.
flush()
를 수동으로 호출이전에는 그저 JPQL 쿼리를 발생시키기 전에 SQL 쓰기 지연 저장소
에 있는 변경 사항들을 데이터베이스에 맞춰주어 정합성을 맞춰주어야 정상적인 결과를 가져올 수 있기 때문에, flush()
가 무조건 발생한다~ 라고 알고 있었다.
그런데 모든 JPQL에 대해서 flush()
를 날려줄까?라는 의문이 들었다. A
엔티티를 1차 캐시에 올려 놓고, 수정 후…
B
엔티티에 대한 JPQL을 호출해도?!@Modifying
을 사용한) 수정, 삭제여도?!각 상황을 한 번 확인해보자!
간단하게
Member
예제 코드를 작성하였다. 엔티티와 레포지토리를 작성하였다.
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Member(String name) {
this.name = name;
}
public void updateName(String name) {
this.name = name;
}
}
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.id = 1")
void myQuery();
}
💡 참고
@DataJpaTest
가 내부에@Transactional
을 가지고 있기 때문에, 테스트의 마지막에 rollback을 해버린다. 따라서 “==================” 아래에 아무런 쿼리가 나가지 않는 것이 정상일 것이다. 이후에는 예제를 간단히 하기 위해서@Test
부분만 나타내겠다. 이제 테스트 코드를 살펴보자.
@DataJpaTest
class MemberTest {
@Autowired private MemberRepository memberRepository;
@Autowired private EntityManager em;
@Test
void flushTest() {
Member member = memberRepository.save(new Member("member"));
em.flush();
em.clear();
Member findMember = memberRepository.findById(member.getId()).get();
findMember.updateName("update");
memberRepository.myQuery();
System.out.println("==================");
}
}
아래는 실행 결과이다. 간단하게 표시 부분 아래에 발생한 쿼리만 나타내었다.
==================
update
member
set
name=?
where
id=?
---
select
m1_0.id,
m1_0.name
from
member m1_0
where
m1_0.id=1
Member
를 1차 캐시에 올려놓고, 수정하고, Member
에 대한 JQPL 조회 쿼리를 발생시키면 JPQL 쿼리를 날리기 전에 flush()
하는 것을 볼 수 있다.
이번엔 추가로
Team
엔티티와 레포지토리를 작성하였다.
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Team(String name) {
this.name = name;
}
}
public interface TeamRepository extends JpaRepository<Team, Long> {
@Query("select t from Team t")
List<Team> myQuery();
}
만약
Member
를 1차 캐시에 올린 후, 수정하고,Team
에 대한 JPQL을 호출하면 어떻게 될까?
@Test
void flushTest() {
Member member = memberRepository.save(new Member("member"));
em.flush();
em.clear();
Member findMember = memberRepository.findById(member.getId()).get();
findMember.updateName("update");
// memberRepository.myQuery();
teamRepository.myQuery();
System.out.println("==================");
}
실행 결과는 다음과 같다. 이 상황에서는
flush()
가 자동으로 호출되지 않음을 확인할 수 있다.
==================
select
t1_0.id,
t1_0.name
from
team t1_0
조회가 아닌 쿼리를 JPQL로 작성한다는 것은 곧, INSERT, UPDATE, DELETE 쿼리를 작성한다는 것이다. 이를 사용하려면
@Modifying
을 활용해야 할 것이다.
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.id = 1")
void myQuery();
@Modifying
@Query("update Member m set m.name = 'hubcreator'")
void memberBulkQuery();
}
테스트 케이스를 작성해보자.
@Test
void flushTest() {
Member member = memberRepository.save(new Member("member"));
em.flush();
em.clear();
Member findMember = memberRepository.findById(member.getId()).orElseThrow();
findMember.updateName("update");
memberRepository.memberBulkQuery();
System.out.println("==================");
}
쿼리를 확인해보면 “==================” 호출 이전에
flush()
가 자동으로 호출됨을 확인할 수 있다. 이전에 설명했듯이@Modifying
의flushAutomatically
속성의 기본값이false
임에도 불구하고, 1차 캐시에 같은 타입의 엔티티가 있으면 자동으로flush()
가 호출된 것을 확인할 수 있다.
update
member
set
name=?
where
id=?
---
update
member
set
name='hubcreator'
==================
그렇다면 다른 타입의 엔티티를 호출하면?!
public interface TeamRepository extends JpaRepository<Team, Long> {
@Query("select t from Team t")
List<Team> myQuery();
@Modifying
@Query("update Team t set t.name = 'hubcreatorTeam'")
void teamBulkQuery();
}
@Test
void flushTest() {
Member member = memberRepository.save(new Member("member"));
em.flush();
em.clear();
Member findMember = memberRepository.findById(member.getId()).orElseThrow();
findMember.updateName("update");
teamRepository.teamBulkQuery();
System.out.println("==================");
}
실행 결과는 다음과 같다. Team
엔티티에 대한 UPDATE 쿼리가 발생했고, Member
에 대한 flush()
가 발생하지 않았음을 확인할 수 있다. 이런 경우에야말로, @Modifying(flushAutomatically = true)
를 사용해야할 때인 것이다.
update
team
set
name='hubcreatorTeam'
==================
이로써 모든 상황에서 JPQL을 호출한다고 flush()
가 자동으로 호출되지 않음을 알 수 있었다. 앞으로 데이터의 정합성에 조금 더 신경쓸 수(!?!?) 있게 되었다 ㅎㅎㅎ.