[Spring] Data JPA 벌크성 수정 쿼리

박준형·2023년 11월 11일
0

Spring

목록 보기
6/17
post-thumbnail

📌순수 JPA 벌크성 수정 쿼리

쿼리 코드

// update 된 데이터의 수를 반환함
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(); // update 해주기
}

인자로 받아온 나이 이상인 사람의 나이를 +1 해서 update 하는 쿼리

테스트 코드

public void bulkUpdate() {
        //given
        memberJpaRepository.save(new Member("member1", 10));
        memberJpaRepository.save(new Member("member2", 19));
        memberJpaRepository.save(new Member("member3", 20));
        memberJpaRepository.save(new Member("member4", 21));
        memberJpaRepository.save(new Member("member5", 40));

        //when
        // 20살 이상인 사람이 3명이므로 resultCount = 3
        int resultCount = memberJpaRepository.bulkAgePlus(20);

        //then
        assertThat(resultCount).isEqualTo(3);
    }
}

📌Data JPA 벌크성 수정 쿼리

쿼리 코드

	@Modifying // 필수! 값 변경을 알리는 어노테이션
    @Query("update Member m set m.age = m.age + 1 where m.age >= :age")
    int bulkAgePlus(@Param("age") int age);

만약 @Modifying을 해주지 않는다면 InvalidDataAccessApiUsageException이 발생한다.

테스트 코드

@Test
public void bulkUpdate() {
        //given
        memberRepository.save(new Member("member1", 10));
        memberRepository.save(new Member("member2", 19));
        memberRepository.save(new Member("member3", 20));
        memberRepository.save(new Member("member4", 21));
        memberRepository.save(new Member("member5", 40));

        //when
        int resultCount = memberRepository.bulkAgePlus(20);

        //then
        assertThat(resultCount).isEqualTo(3);
}

벌크 연산시 주의점❗

JPA는 영속성 컨텍스트(1차 캐시)가 있는데 벌크 연산을 하게 되면 이 영속성 컨텍스트를 무시하고 바로 DB에 반영하게 된다.

	@Test
    public void bulkUpdate() {
        //given
        memberRepository.save(new Member("member1", 10));
        memberRepository.save(new Member("member2", 19));
        memberRepository.save(new Member("member3", 20));
        memberRepository.save(new Member("member4", 21));
        memberRepository.save(new Member("member5", 40));

        //when
        int resultCount = memberRepository.bulkAgePlus(20);

        List<Member> members = memberRepository.findByUsername("member5");
        Member member5 = members.get(0);
        System.out.println(member5);

        //then
        assertThat(resultCount).isEqualTo(3);
    }

given절을 보면 멤버 데이터 5개를 저장하게 된다. 여기서 JPA는 이 데이터들을 영속성 컨텍스트에 저장하게 되고 벌크 연산(20살 이상인 멤버의 나이를 +1 하는 쿼리)을 실행하면 벌크 연산된 데이터가 DB에 바로 반영된다. 이후 findByUsername으로 member5를 조회해서 나이를 확인하면 41살이 아닌 영속성 컨텍스트에 저장된 40살이 조회된다. 이는 JPA가 값을 조회할 때 영속성 컨텍스트를 먼저 확인하고 없을 때 DB에 접근하기 때문이다.

만약 같은 트랜잭션 내에서 벌크 연산 수행후 해당 값을 조회하는 로직을 사용한다면?

✅EntityManager 사용

@PersistenceContext
EntityManager em;
    
int resultCount = memberRepository.bulkAgePlus(20);
em.flush(); // 영속성 컨텍스트에 있는 데이터 변경 내용 DB에 반영
em.clear(); // 영속성 컨텍스트 비우기

EntitiyManager의 flush(영속성 컨텍스트의 변경 내용 DB에 반영 -> JPA는 쿼리 실행전에 영속성 컨텍스트의 내용을 DB에 반영하기 때문에 사실상 없어도 된다. 하지만 마이바티스나 JDBC를 사용한다면 작성해야 한다.), clear(영속성 컨텍스트 비우기)를 사용해서 영속성 컨텍스트를 비워서 영속성 컨텍스트에서 1차 조회 하는 것을 DB로 조회하게 만들어 줘야한다.

✅Data JPA 사용

@Modifying(clearAutomatically = true) // 값 변경을 알리는 어노테이션
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);

@Modifying에 clearAutomatically 옵션을 true로 설정한다면 자동으로 영속성 컨텍스트를 비우게 된다.

profile
으쌰 으쌰

0개의 댓글

관련 채용 정보