Spring Data JPA - 벌크성 수정 쿼리

2022. 7. 31. 03:09·공부/JPA
반응형

출처 : 인프런 실전! 스프링 데이터 JPA

Spring Data JPA 게시글은 대부분 인프런의 김영한님의 강의인 '실전! 스프링 데이터 JPA' 기반으로 내용을 정리했습니다.

 

벌크성 수정 쿼리

JPA는 엔티티를 가져와서 변경할 경우 변경 감지 기능이 작동한다. 이런 경우는 한 건 씩 지원되는 거고, 모든 데이터에 일괄적인 업데이트를 날려야 하는 경우(ex 모든 직원의 연봉 10% 인상)에 벌크성 수정 쿼리라고 한다.

 

순수 JPA 예제

  • 20살이거나, 20살 이상인 회원의 나이를 +1
  • MemberJpaRepository.class
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(); // 응답 값의 개수 나옴
}
  • MemberJpaRepositoryTest.class
@Test
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
    int resultCount = memberJpaRepository.bulkAgePlus(20);

    // then
    assertThat(resultCount).isEqualTo(3);
}
update
    member 
set
    age=age+1 
where
    age>=?

DB 확인

 

 

Spring Data JPA 예제

Spring Data JPA에서는 @Modifying 애노테이션과 반환 값이 int이기만 하면 된다.

  • MemberRepository.interface
@Modifying // jpa executeUpdate 같은 기능 필수임
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
  • MemberRepositoryTest.class
@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);
}
update
    member 
set
    age=age+1 
where
    age>=?

벌크성 수정, 삭제 쿼리는 @Modifying 애노테이션을 사용해야 한다. 사용하지 않으면 org.hibernate.hql.internal.QueryExceptionRequestException: Not supported for DML operations 에러가 발생한다. 그러므로 꼭 필수로 넣어줘야 하는 애노테이션이다.

 

벌크성 쿼리의 주의할 점

JPA를 사용하면 엔티티들은 영속성 컨텍스트에서 관리가 된다. 벌크성 쿼리는 이 영속성 컨텍스트를 무시하기 때문에 영속성 컨텍스트에 있는 엔티티의 값들과 DB에 있는 값이 달라질 수 있다.

  • MemberRepositoryTest.class
@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> result = memberRepository.findByUsername("member5");
    Member member = result.get(0);
    System.out.println("member = " + member);

    // then
    assertThat(resultCount).isEqualTo(3);
}
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.username as username3_0_ 
    from
        member member0_ 
    where
        member0_.username=?
member = Member(id=5, username=member5, age=40)

위 테스트를 실행하면 충격적이게도 member에 있는 값이 41이 아니라 40으로 출력된다. 이렇게 DB와 영속성 컨텍스트에 있는 값이 다를 수 있다. 이런 경우를 방지하기 위해 벌크 연산 후에는 영속성 컨텍스트를 아예 초기화시켜서 영속성 컨텍스트에서 DB의 값을 가져오도록 해야 한다.

영속성 컨텍스트를 초기화하기 위해 맨 윗줄에 EntityManager를 가져올 수 있도록 코드를 추가한다.

@PersistenceContext
EntityManager em;
@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);
    
 //   em.flush(); // 남아있는 변경되지 않는 내용을 DB에 반영. 여기서는 굳이 안 해도 된다.
    em.clear(); // 초기화!

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

    // then
    assertThat(resultCount).isEqualTo(3);
}
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.username as username3_0_ 
    from
        member member0_ 
    where
        member0_.username=?
member = Member(id=5, username=member5, age=41)

 

Spring Data JPA 영속성 컨텍스트 초기화

Spring Data JPA에서는 굳이 em.clear() 명령을 적을 필요가 없다. 그러므로 주석 처리 후 아래 코드를 추가하자.

  • MemberRepository.interface
@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로(기본값은 false) 주면 굳이 따로 em.clear()를 하지 않아도 자동으로 update 쿼리가 발생된 후 영속성 컨텍스트를 초기화해준다.

 

위 문제점에 대한 방안

  1. 영속성 컨텍스트에 엔티티가 없는 상태에서 벌크 연산을 먼저 실행한다.
  2. 부득이하게 영속성 컨텍스트에 엔티티가 있으면 벌크 연산 직후 영속성 컨텍스트를 초기화한다.
반응형
저작자표시 비영리 변경금지 (새창열림)
'공부/JPA' 카테고리의 다른 글
  • Spring Data JPA - JPA Hint & Lock
  • Spring Data JPA - @EntityGraph
  • Spring Data JPA - 스프링 데이터 JPA 페이징과 정렬
  • Spring Data JPA - 순수 JPA 페이징과 정렬
데부한
데부한
어차피 할 거면 긍정적으로 하고 싶은 개발자
    반응형
  • 데부한
    동동이개발바닥
    데부한
  • 전체
    오늘
    어제
    • 분류 전체보기 (307)
      • 방통대 컴퓨터과학과 (27)
        • 잡담 (9)
        • 3학년1학기 (17)
      • 프로젝트 및 컨퍼런스 회고 (1)
        • 프로젝트 (4)
        • 한이음 프로젝트 (0)
        • 회고 (3)
      • 공부 (165)
        • Spring (37)
        • JPA (71)
        • 인프런 워밍업 클럽_BE (10)
        • Java (6)
        • React.js (27)
        • 넥사크로 (11)
        • 기타 (3)
      • 알고리즘 (85)
        • 알고리즘 유형 (10)
        • 알고리즘 풀이 (57)
        • SQL 풀이 (18)
      • 에러 해결 (13)
      • 잡담 (7)
        • 국비교육 (2)
        • 구매후기 (5)
        • 진짜 잡담 (0)
  • 블로그 메뉴

    • Github
    • Linkedin
    • 홈
    • 방명록
    • 글쓰기
    • 관리
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    SpringBoot를 이용한 RESTful Web Service 개발
    oracle
    토이프로젝트
    스프링부트
    인프런
    자바스크립트
    MSA
    프론트엔드
    react
    QueryDSL
    토비의스프링부트
    프로그래머스
    개발자
    기출문제
    방통대
    Spring
    전자정부프레임워크
    에러해결
    넥사크로
    RESTful
    JPA
    IT
    springboot
    SQL
    egov
    Java
    운영체제
    백준
    코딩테스트
    알고리즘
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
데부한
Spring Data JPA - 벌크성 수정 쿼리
상단으로

티스토리툴바