스프링 데이터 JPA는 유연한 반환 타입을 지원한다.
List<Member> findByUsername(String name); //컬렉션
Member findByUsername(String name); //단건
Optional<Member> findByUsername(String name); //단건 Optional
null
반환javax.persistence.NonUniqueResultException
예외 발생📌 페이징, 정렬 조건 예시
- 검색 조건: 나이 10살
- 정렬 조건: 이름으로 내림차순
- 페이징 조건: 첫 번째 페이지, 페이지 당 보여줄 데이터는 3건
public List<Member> findByPage(int age, int offset, int limit) {
return em.createQuery("select m from Member m where m.age = :age order by m.username desc")
.setParameter("age", age)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
}
public long totalCount(int age) {
return em.createQuery("select count(m) from Member m where m.age = :age", Long.class)
.setParameter("age", age)
.getSingleResult();
}
📌 페이징과 정렬 파라미터
org.springframework.data.domain.Sort
: 정렬 기능org.springframework.data.domain.Pageable
: 페이징 기능 (내부에Sort
포함)
📌 특별한 반환 타입
org.springframework.data.domain.Page
: 추가count
쿼리 결과를 포함하는 페이징org.springframework.data.domain.Slice
: 추가count
쿼리 없이 다음 페이지만 확인 가능
- 내부적으로
limit
+ 1 조회List
(자바 컬렉션): 추가count
쿼리 없이 결과만 반환
Page<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용
Slice<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 X
List<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 X
List<Member> findByUsername(String name, Sort sort);
🔗 코드 확인하기
Slice
테스트 실행 결과limit
가 4로 나온다.countQuery
테스트 실행 결과left join
없음JPA
이용public int bulkAgePlus(int age) {
int resultCount = em.createQuery(
"update Member m set m.age = m.age + 1" +
"where m.age >= :age")
.setParameter("age", age)
.executeUpdate();
return resultCount;
}
20살 이상인 사람만 age
+ 1하는 테스트 완료!
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
이 있어야 한다.@Modifying(clearAutomatically = true)
false
false
로 설정하면
중간에 찍어보면 member5
의 age
가 41
이 아닌 40
으로 나온다. → bulk
연산 이후 영속성 컨텍스트를 날려버려야 한다!
🔼 이건 Entity Manager
로 해결한 경우
🔼 이건 clearAutomatically
설정을 true
로 한 경우
@EntityGraph
]연관된 엔티티들을 SQL 한번에 조회하는 방법
member
→ team
은 지연로딩(LAZY
) 관계이다.
따라서 다음과 같이 team
의 데이터를 조회할 때마다 쿼리가 실행한다 (N+1
문제 발생)
@Test
public void findMemberLazy() throws Exception {
//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
List<Member> members = memberRepository.findAll();
//then
for (Member member : members) {
System.out.println("member = " + member.getUsername());
}
}
member
와 team
이 각각 조회된다.
→ 연관된 엔티티를 한번에 조회하려면 페치 조인이 필요하다.
JPQL fetch join
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
//공통 메서드 오버라이드
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
//JPQL + 엔티티 그래프
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
//메서드 이름으로 쿼리에서 특히 편리하다.
@EntityGraph(attributePaths = {"team"})
List<Member> findEntityGraphByUsername(@Param("username") String username);
→ left outer join
을 사용한다!
JPA Hint & Lock
]JPA Hint
JPA 쿼리 힌트 (SQL 힌트가 아닌 JPA 구현체에게 제공하는 힌트)
@QueryHints(value = @QueryHint(name = "org.hibernate.readOnly", value = "true"))
Member findReadOnlyByUsername(String username);
Lock
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findLockByUsername(String name);
Lock
을 걸면 안된다.