데이터 스프링 JPA - 쿼리 메소드 기능(벌크성 수정 쿼리, @EntityGraph, JPA Hint & Lock)

2023. 8. 31. 20:18·Back-End/Spring Data JPA

실전! 스프링 데이터 JPA - 인프런 | 강의

 

실전! 스프링 데이터 JPA - 인프런 | 강의

스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제공합니다.

www.inflearn.com

벌크성 수정 쿼리

  • 벌크성 쿼리는 대량의 엔티티 속성을 수정해야 할 때, 사용하는 기능이다.
  • ex) 월급이 300 이상인 사원들의 연봉 10% 인상을 DB에 반영 등

일반 JPA 벌크성 수정 쿼리

public int buikAgePlus(int age) {
        return em.createQuery("update Member m set m.age = m.age + 1 where m.age >= :age")
                 .setParameter("age", age)
                 .executeUpdate();
    }
  • 파라미터의 age 값보다 큰 Member의 age를 + 1 시켜주는 쿼리이다.

테스트 코드

    @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.buikAgePlus(20);

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

    }

스프링 데이터 JPA 벌크성 수정 쿼리

@Modifying
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);

테스트 코드

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);
}

  • @Modifying 어노테이션 사용
    • 미사용시 org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations 예외 발생
  • 벌크성 쿼리를 실행하고 나서 영속성 컨텍스트 초기화 : @Modifying(clearAutomatically = true)
    • 해당 옵션 없이 회원을 findById로 다시 조회하면, 영속성 컨텍스트에 과거 값이 남아서 문제가 될 수 있으므로, 다시 조회해야 한다면, 영속성 컨텍스트 초기화를 해야한다.

권장하는 방안

  1. 영속성 컨텍스트에 엔티티가 없는 상태에서 벌크 연산을 먼저 실행
  2. 부득이하게 영속성 컨텍스트에 엔티티가 있으면 벌크 연산 직후 영속성 컨텍스트 초기화

@EntityGraph (fetch 조인)

  • 연관된 엔티티들을 SQL 한번에 조회한다.
  • 현재 member → team은 지연로딩 관계이므로, team을 조회할 때 마다 쿼리가 실행된다.
@Test
    public void findMemberLazy() {
        //given
        //member1 -> teamA
        //member2 -> teamB

        Team teamA = new Team("teamA");
        Team teamB = new Team("teamB");
        teamRepository.save(teamA);
        teamRepository.save(teamB);
        Member member1 = new Member("member1", 10, teamA);
        Member member2 = new Member("member2", 10, teamB);
        memberRepository.save(member1);
        memberRepository.save(member2);

        em.flush();
        em.clear();

        //when
        //select Member 실행
        List<Member> members = memberRepository.findAll();

        for (Member member : members) {
            System.out.println("member = " + member.getUsername());
            //select Team 실행 -> Lazy 로딩시 N + 1 문제 발생
            System.out.println("member = " + member.getTeam().getName());
        }
    }
  • 해당 테스트를 실행하면, N + 1 문제가 발생하여 쿼리가 총 3번 실행된다. (member는 실제 객체로 쿼리 한번에 member1과 2를 조회해오지만, 각각의 member에 연관된 team은 가짜인 프록시 객체로 조회해 오기때문에, 각각 연관되어 있는 team에대해 쿼리가 나간다.)

JPQL 페치 조인

@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
  • @Query를 이용해서 페치조인 쿼리를 작성해서 조회할 수 있다.

EntityGraph

// 공통 메서드 오버라이드
@Override 
@EntityGraph(attributePaths = {"team"})
List<Member> findAll(); 

//JPQL + 엔티티 그래프
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();

//username 파라미터를 받는 메서드 이름 쿼리
@EntityGraph(attributePaths = {"team"}) 
List<Member> findEntityGraphByUsername(@Param("username") String username);

  • @EntityGraph 를 이요하면, 페치조인을 간편하게 사용 할 수 있다.

NamedEntityGraph 사용

// Member엔티티에 NamedEntityGraph 선언
@NamedEntityGraph(name = "Member.all", attributeNodes =
@NamedAttributeNode("team"))
@Entity
public class Member {}

// 리포지토리에서 선언한 NamedEntityGraph 이용
@EntityGraph("Member.all")
@Query("select m from Member m")
List<Member> findMemberEntityGraph();

JPA Hint & Lock

Hint

  • 조회해서 가져온 엔티티가 영속성 컨텍스트에 캐싱 되어 있는 상태일 때, 해당 엔티티의 내용을 수정하면, JPA의 변경감지 기능에 의해서 update쿼리가 수행되는데, 이를 방지하기 위해 조회기능만 적용하기 위해서 사용한다.
@QueryHints(value = {@QueryHint(name = "org.hibernate.readOnly", value = "true")}, forCounting = true)
Member findReadOnlyByUsername(String username);
  • org.springframework.data.jpa.repository.QueryHints 어노테이션 사용
  • forCounting : 반환 타입으로 Page 인터페이스를 적용하면 추가로 호출하는 페이징을 위한 count 쿼리도 쿼리 힌트 적용(기본값 true)

Lock

@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findLockByUsername(String username);
  • org.springframework.data.jpa.repository.Lock
  • JPA가 제공하는 락은 JPA 책 16.1 트랜잭션과 락 절을 참고

정리

벌크성 수정 쿼리

  • 대량의 엔티티 속성을 수정해야할 때 사용가능
  • @Modyfiying 사용
  • 벌크 연산 직후 영속성 컨텍스트 초기화

@EntityGraph 페치조인

  • 지연 로딩 관계인 엔티티들을 조회할 때, N + 1 문제를 방지하기 위해 페치조인을 간편하게 사용하는 기능
  • @EntityGraph 사용

Hint & Lock

  • Hint : 영속성 컨텍스트에 캐싱된 엔티티의 변경 감지기능을 사용하지 않고 조회기능만 사용 할 수 있게 적용하는 기능
반응형
저작자표시 비영리 (새창열림)
'Back-End/Spring Data JPA' 카테고리의 다른 글
  • 스프링 데이터 JPA - 쿼리 메소드 기능(페이징과 정렬)
  • 스프링 데이터 JPA - 쿼리 메소드 기능(쿼리 메소드 이름, @NamedQuery, @Query)
  • 스프링 데이터 JPA - 공통 인터페이스(JpaRepository) 기능 생성
  • 스프링 데이터 JPA - 프로젝트 환경설정 및 예제 도메인 모델 작성
LightSource
LightSource
어제보단 발전한 오늘의 나를 위한 블로그
    반응형
  • LightSource
    LightSourceCoder
    LightSource
  • 전체
    오늘
    어제
    • 분류 전체보기 (152)
      • Git (4)
      • Language (6)
        • Java (6)
      • Back-End (63)
        • Spring Boot (4)
        • MyBatis (1)
        • Oracle (1)
        • PL SQL (3)
        • JPA (26)
        • Spring Data JPA (5)
        • Spring MVC (8)
        • Spring (12)
        • Spring Security (2)
        • Redis (1)
      • Front-End (38)
        • 아이오닉 (2)
        • JSP (7)
        • JavaScript (4)
        • React (16)
        • TypeScript (3)
        • Angular (6)
      • AWS (1)
      • CI & CD (1)
      • 개발지식 (13)
        • 네트워크 (9)
        • CS 지식 (4)
      • 데이터모델링 (2)
      • Tool (1)
      • 프로젝트 (5)
      • 독후감 (2)
      • 잡생각 (0)
      • 면접 준비 (1)
      • 알고리즘 (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    배열요소수정
    react
    배열요소삭제
    배요소열추가
    리액트
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
LightSource
데이터 스프링 JPA - 쿼리 메소드 기능(벌크성 수정 쿼리, @EntityGraph, JPA Hint & Lock)
상단으로

티스토리툴바