728x90
순수 JPA 사용
조건에 맞는 엔티티들의 값을 일괄 수정하고 싶다면 벌크 연산을 사용하면 된다
public List<Member> bulkAgePlus(int age) {
return em.createQuery("update Member m set m.age = m.age + 1 where m.age >= : age", Member.class)
.setParameter("age", age)
.getResultList();
}
public List<Member> bulkAgePlus(int age) {
return em.createQuery("update Member m set m.age = m.age + 1 where m.age >= : age")
.setParameter("age", age)
.getResultList();
}
위와 같이 코드를 작성하고 테스트를 돌려보자
@Test
void bulkUpdate() throws Exception {
//given
memberJpaRepository.save(new Member("member1", 1));
memberJpaRepository.save(new Member("member2", 2));
memberJpaRepository.save(new Member("member3", 3));
memberJpaRepository.save(new Member("member4", 4));
memberJpaRepository.save(new Member("member5", 5));
//when
List<Member> members = memberJpaRepository.bulkAgePlus(3);
for (Member member : members) {
System.out.println("member = " + member);
}
}
update나 delete 쿼리는 타입으로 반환될 수 없고, 두번째 코드에서는 해당 DML을 지원하지 않는다고 한다
그래서 아래의 코드와 같이 executeUpdate 메서드를 이용하여 벌크 연산을 수행해야한다
public int bulkAgePlus(int age) {
return em.createQuery("update Member m set m.age = m.age + 1 where m.age >= : age")
.setParameter("age", age)
.executeUpdate();
}
테스트 코드를 돌리고 DB를 보면 age가 3이상인 엔티티들의 age가 1만큼 늘어났다
executeUpdate()
- update나 delete statement를 execute하는 메서드
- 삭제되거나 업데이트 된 엔티티의 개수를 반환한다
Spring data JPA 사용
@Test
void bulkUpdate() throws Exception {
//given
memberRepository.save(new Member("member1", 1));
memberRepository.save(new Member("member2", 2));
memberRepository.save(new Member("member3", 3));
memberRepository.save(new Member("member4", 4));
memberRepository.save(new Member("member5", 5));
//when
int resultCount = memberRepository.bulkAgePlus(3);
//then
assertThat(resultCount).isEqualTo(3);
}
@Modifying이 없을 경우
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
위와 같은 에러가 발생한다
@Modifying
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
@Modifying?
- @Query 주석을 통해 정의된 쿼리 메서드에 사용되는 경우에만 사용
- INSERT, UPDATE, DELETE 및 DDL 문에서 필요
@Modifying
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
List<Member> bulkAgePlus(@Param("age") int age);
궁금해서 반환형을 int가 아닌 List<Member>로 변경했으나 위와 똑같은 에러 발생
벌크 연산 시 조심해야할 점
@Test
void bulkUpdate() throws Exception {
//given
memberRepository.save(new Member("member1", 1));
memberRepository.save(new Member("member2", 2));
memberRepository.save(new Member("member3", 3));
memberRepository.save(new Member("member4", 4));
memberRepository.save(new Member("member5", 5));
//when
int totalCount = memberRepository.bulkAgePlus(3);
List<Member> members = memberRepository.findByUsername("member3");
Member member = members.get(0);
System.out.println("member = " + member);
//then
assertThat(totalCount).isEqualTo(3);
}
출력한 member3의 age와 DB에 저장된 age는 왜 다를까?
DB에 저장된 member3의 age는 4, 출력한 age는 3이 된다
- 결론을 말하자면 bulkAgePlus 쿼리 메서드는 DB에 직접 쿼리를 날리는 것이다
- 즉, member들은 영속성 컨텍스트에 의해 관리되는데 벌크 연산은 그걸 무시하고 바로 DB에 쿼리를 날린다
- 따라서 영속성 컨텍스트에 존재하는 엔티티의 age는 update 되지 않는다
- flush를 해주어야 값이 변하게 된다
영속성 컨텍스트를 clear 해주면 age 값을 출력할 때 4를 볼 수 있다
그렇다면 벌크 연산을 실행할 때마다 항상 EntityManager를 주입 받아야할까?
그렇지 않다
- @Modifying에 clearAutomatically 옵션 true 설정
- 수정 쿼리를 실행한 후 영속성 컨텍스트를 clear 해준다
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
REFERENCES
728x90
'Spring 강의 > Spring data JPA' 카테고리의 다른 글
@EntityGraph (0) | 2022.04.13 |
---|---|
페이징과 정렬 (0) | 2022.04.11 |
쿼리 메서드 (0) | 2022.04.08 |