벌크성 수정 쿼리
쿼리
// MemberRepository, / 벌크 쿼리
@Modifying // 이게 있어야 업데이트 쿼리를 날려줌.
@Query("update Member m set m.age = m.age + 1 where m.age >=:age")
int bulkAgePlus(@Param("age")int age);
Test 코드
- 벌크 쿼리는 영속성 컨텍스트에 들리지 않고 DB에 바로 쿼리를 날림. 영속성 컨텍스트는 영향을 받지 않음.
- 그래서 아래와 같이 사용X, 벌크 쿼리 사용 후에는 영속성 컨텍스트를 날려주어야함.
@Test
public void paging(){
Member m1 = new Member("AAA1",20);
Member m2 = new Member("AAA2",30);
Member m3 = new Member("AAA3",40);
Member m4 = new Member("AAA4",10);
Member m5 = new Member("AAA5",22);
memberRepository.save(m1);
memberRepository.save(m2);
memberRepository.save(m3);
memberRepository.save(m4);
memberRepository.save(m5);
int resultCount = memberRepository.bulkAgePlus(20);
// 벌크 연산은 영속성 컨텍스트를 들리지 않고 DB에 쿼리를 바로 날리는 식
// 영속성 컨텍스트에 있는 member들은 영향을 받지 않고,
// DB에 있는 member들은 영향을 받음.
Member aaa5 = memberRepository.findMemberByUsername("AAA5");
System.out.println("aaa5 age = " + aaa5.getAge()); // 22,
System.out.println(resultCount); // 4
}
수정 코드
- 방법1: flusth와 clear 사용
@Test
public void paging(){
Member m1 = new Member("AAA1",20);
Member m2 = new Member("AAA2",30);
Member m3 = new Member("AAA3",40);
Member m4 = new Member("AAA4",10);
Member m5 = new Member("AAA5",22);
memberRepository.save(m1);
memberRepository.save(m2);
memberRepository.save(m3);
memberRepository.save(m4);
memberRepository.save(m5);
entityManager.flush();
entityManager.clear();
int resultCount = memberRepository.bulkAgePlus(20);
Member aaa5 = memberRepository.findMemberByUsername("AAA5");
System.out.println("aaa5 age = " + aaa5.getAge()); // 23
System.out.println(resultCount); // 4
}
- 방법2: clearAutomatically = true 사용
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >=:age")
int bulkAgePlus(@Param("age")int age);
@EntityGraph
지연 로딩 전략
- team의 name을 호출할 때마다 쿼리 발생. -> 성능 이슈
@Test
public void findMemberLazy(){
// 멤버와 팀은 다대1 지연관계 전략
// 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);
entityManager.flush();
entityManager.clear();
List<Member> members = memberRepository.findAll();
for(Member member : members){
System.out.println("member = " + member.getUsername()); // 이때는 문제가 없음
System.out.println("member.team = " + member.getTeam().getName()); // 쿼리 발생
}
}
fetch join
- 쿼리가 한 번만 나가는 것을 볼 수 있다.
// member repository에 쿼리 작성
@Query("select m from Member m left join fetch m.team ")
List<Member> findMemberFetchJoin();
@Test
public void findMemberLazy(){
// 멤버와 팀은 다대1 지연관계 전략
// 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);
entityManager.flush();
entityManager.clear();
List<Member> members = memberRepository.findMemberFetchJoin(); // 이 때 데이터를 모두 조회
for(Member member : members){
System.out.println("member = " + member.getUsername());
System.out.println("member.team = " + member.getTeam().getName());
}
}
EntityGraph
- 위 코드에서 findAll()로 멤버 조회
- 결과는 위와 같음.
- EntityGraph를 사용하면 fetch 조인을 한 것과 같음.
// member repository에 EntityGraph 어노테이션 사용
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
List<Member> members = memberRepository.findAll(); // 이 때 데이터를 모두 조회
for(Member member : members){
System.out.println("member = " + member.getUsername());
System.out.println("member.team = " + member.getTeam().getName());
}
Hint
- entityManager.flush(); // 변경 감지로 update쿼리가 나감
변경 감지를 사용하려면 원본과 수정이 된 객체 , 즉 2개가 필요함 -> 메모리 사용
근데 변경을 하지 않을 상황에서도 조회했다고 메모리를 먹을 수 있음.
@Test
public void queryHint(){
Member member1 = memberRepository.save(new Member("member1", 10));
entityManager.flush();
entityManager.clear();
Member findMember = memberRepository.findById(member1.getId()).get();
findMember.setUsername("member2");
entityManager.flush(); // 변경 감지로 update쿼리가 나감
// 변경 감지를 사용하려면 원본과 수정 객체 2개가 필요함 -> 메모리 사용
// 근데 변경을 하지 않을 상황에서도 조회했다고 메모리를 먹을 수 있음.
}
@QueryHints 사용
- 읽기 전용 객체 불러오기
// member repository
@QueryHints( value = @QueryHint(name = "org.hibernate.readOnly", value = "true"))
Member findReadOnlyByUsername(String user);
@Test
public void queryHint(){
Member member1 = memberRepository.save(new Member("member1", 10));
entityManager.flush();
entityManager.clear();
// 읽기 전용
Member findMember = memberRepository.findReadOnlyByUsername("member1");
findMember.setUsername("member2"); // 변경 감지가 일어나지 않음.
// 읽기 전용이라 메모리 낭비가 없다.
entityManager.flush();
}
'JPA' 카테고리의 다른 글
[Querydsl] 환경설정 및 기본 문법 (0) | 2024.02.05 |
---|---|
[Data JPA] 확장 기능 (0) | 2024.01.31 |
[Data JPA] 기본적인 사용법과 Dto 조회하기, 파라미터 바인딩 (1) | 2024.01.26 |
[JPA API] 컬렉션 조회 최적화 (0) | 2024.01.25 |
[JPA API] 주문, 배송정보, 회원 조회 API (0) | 2024.01.24 |