스터디 2회차
✅ 범위 : 쿼리 메소드 기능 - 스프링 데이터 JPA 페이징과 정렬까지
단축키 → shift + F6: 한 꺼번에 이름 바꾸기
1. 쿼리 메소드 기능
메소드 이름으로 쿼리 생성
1.1 범위 지정
- 쿼리 메소드를 사용해 조건에 맞는 데이터를 쉽게 조회할 수 있다.
- 쿼리 메소드에서 제공하는 키워드를 사용해 정확한 범위를 지정할 수 있다.
1.2 쿼리 메소드 필터 조건(Distinct, And, Or 등..)
Spring Data JPA - Reference Documentation
1.3예시
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByEmailAndName(String email, String name);
List<Member> findByAgeGreaterThan(int age);
List<Member> findByAgeBetween(int start, int end);
}
1.4 스프링 데이터 JPA가 제공하는 쿼리 메소드 기능
- 조회 : find…By, → findHelloBy 처럼 By 앞에 설명이 들어갈 수 있음, By 뒤에 아무것도 없다면 전체 조회!
- COUNT: count...By 반환타입 long
- EXISTS: exists...By 반환타입 boolean
- 삭제: delete...By, remove...By 반환타입 long
- DISTINCT: findDistinct, findMemberDistinctBy
- LIMIT: findFirst3, findFirst, findTop, findTop3
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
- 단점은 파라미터가 증가하면 메서드 이름이 매우 지저분해진다.
JPA NamedQuery
- 실무에서 거의 사용 x
- @NameQuery(name = “엔티티명. 메소드명”, query = )
- 가장 큰 장점 → 정적 쿼리기 때문에, 로딩 시점에 파싱을 다 해본다. 그래서 로딩 시점에 버그를 잡을 수 있다!!
🌟🔥 @Query 어노테이션을 사용해서 리파지토리 메소드에 쿼리 직접 정의
- 메소드에 JPQL 쿼리 작성(실무에서 많이 씀, 그리고 김영한님이 권장하시는 기능임!!)
@Query("select m from Member m where m.username= :username and m.age = :age")
List<Member> findUser(@Param("username") String username, @Param("age") int
age);
@Query, 값, Dto 조회
- Dto 생성해서 반환하는 방법
- 주의) new 명령어 사용, 생성자가 맞는 Dto이여야 함.
@Query("select new study.datajpa.dto.MemberDto(m.id, m.username, t.name) " +
"from Member m join m.team t")
List<MemberDto> findMemberDto();
파라미터 바인딩
- 파라미터 바인딩은 가급적 이름기반으로 사용하자 → select m from Member m where m.username = :name (코드 가독성과, 유지보수를 위해서)
- 컬랙션 파라미터 바인딩
List<Member> findByNames(@Param("names") List<String> names);
- Collection 타입으로 in절 지원 @Query("select m from Member m where m.username in :names")
스프링 데이터 JPA 페이징과 정렬
① repository
- pageable은 페이지에 대한 정보를 담는다.
- 반환타입이 Page<>이면 totalCount도 전달된다.
public interface MemberRepository extends Repository<Member, Long> {
Page<Member> findByAge(int age, Pageable pageable);
}
② pageable
- pageRequest(구현체) 부모가 pageable 인터페이스다.
PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC,
"username"));
- 참고: 성능 최적화를 위한, count 쿼리 분리
- 분리를 안 하면 아래코드처럼 join할 때 사진과 같이 count 할 때에는 join을 할 필요가 없지만 하게 된다.
@Query(value = “select m from Member m left join m.team t”,
countQuery = “select count(m.username) from Member m”)
Page<Member> findMemberAllCountBy(Pageable pageable);
- ❗️Page<> 타입은 api에 그대로 반환x, dto로 반환 → 엔티티는 무조건 외부 노출해서는 안 된다. → Page<MemberDto> dtoPage = page.map(m -> new MemberDto());
관련 메소드
- page.getTotalElements() : 전체 데이터 수 (count)
- page.getNumber() : 현재 페이지 번호
- page.getTotalPages() : 전체 페이지 번호
- page.isFirst() : 첫 페이지인지 확인
- page.hasNext() : 다음 페이지가 있는지 확인
✔️ CHECK LIST
JPQL
: 테이블이 아닌 엔티티 객체 를 대상으로 검색하는 객체지향 쿼리(sql을 추상화한 것→ 특정 데이터베이스에 의존x)
- sql : 데이터베이스 테이블을 대상으로 쿼리를 질의
- 유의사항
- from절에 들어가는 것은 객체다!
- select m from Member m where m.age > 8
- 엔티티와 속성은 대소문자를 구분
- 예를 들면, Member 엔티티와 username 필드
- JPQL 키워드는 대소문자 구분 안함
- SELECT, FROM, where
- 엔티티 이름을 사용한다. 테이블 이름이 아니다.
- 엔티티명 Member
- 별칭은 필수이다.
- Member의 별칭 m
List 반환 타입
- 컬렉션 조회시 결과가 없을 경우, null 값을 반환하는 것이 아니라 빈 컬렉션을 반환한다.
- 반면 단건 조회시에는 결과가 없을 경우, null을 반환한다.
List<Member> result = findByUsername(String name); //컬렉션
if (result != null) {
// 이런 코드는 절대 사용x (불필요한 코드임, result는 null일 수가 없다.)
}
- result.isEmpty()를 사용하면 될 것 같다.
- == , equals() 차이 나중에 찾아보기
그 외의 것들
- 엔티티에는 @Data 쓰지 않는 게 좋다 → @getter, @setter 모두 포함하기 때문에
- db에 데이터가 있을 수도 있고 없을 수도 있으면 Optional 쓰자
'백엔드 > SpringBoot' 카테고리의 다른 글
스프링 AOP와 트랜잭션 (0) | 2025.02.12 |
---|---|
멀티스레드 환경에서의 트랜잭션 동작 - feat. 테스트 코드 (0) | 2025.02.10 |
② 실전! 스프링 부트와 JPA 활용1 (1) | 2023.11.14 |
① 실전! 스프링 부트와 JPA 활용1 (2) | 2023.11.06 |
① JPA 공부 By 실전! 스프링 데이터 JPA (0) | 2023.03.10 |