- 쿼리 메소드 기능 3가지
1. 메소드 이름으로 쿼리 생성
2. 메소드 이름으로 JPA NamedQuery 호출
3. @Query 어노테이션을 사용해서 Repository 인터페이스에 쿼리 직접 정의
- 메소드 이름으로 쿼리 생성
- 순수 JPA Repository
public List<Member> findByUsernameAndAgeGreaterThan(String username, int age) {
return em.createQuery("select m from Member m where m.username = :username
and m.age > :age")
.setParameter("username", username)
.setParameter("age", age)
.getResultList();
}
- 스프링 데이터 JPA
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}
- 스프링 데이터 JPA를 이용하면 메소드 이름을 분석해서 JPQL을 생성하고 실행한다.
- 스프링 데이터 JPA가 제공하는 쿼리 메소드 기능
1. 조회 : find...By / read...By / query...By / get...By
1.1. ex) findHelloBy처럼 ...에 식별하기 위한 내용(설명)이 들어가도 된다.
2. COUNT : count...By - 반환타입 long
3. EXISTS : exists...By - 반환타입 boolean
4. 삭제 : delete...By, remove...By - 반환타입 long
- 이 기능은 엔티티의 필드명이 변경되면 인터페이스에 정의한 메서드 이름도 꼭 함께 변경해야 한다. 그렇지 않으면 애플리케이션을 시작하는 시점에 오류가 발생한다.
- 이렇게 애플리케이션 로딩 시점에 오류를 인지할 수 있는 것이 스프링 데이터 JPA의 매우 큰 장점이다.
- @Query, 리포지토리 메소드에 쿼리 정의하기
- MemberRepository에 작성
@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에 해당하는 부분에 JPQL을 작성함으로서 메서드명과 별개로 쿼리를 작성 할 수 있다.
- @Param을 이용하여 파라미터로 값을 넣을 수 있다.
@Test
public void testQUery() {
Member m1 = new Member("AAA", 10);
Member m2 = new Member("AAA", 20);
memberRepository.save(m1);
memberRepository.save(m2);
List<Member> result = memberRepository.findUser("AAA", 10);
assertThat(result.get(0)).isEqualTo(m1);
}
- @Query는 쿼리 조건이 여러개이고, 메서드 이름이 지저분해질 경우 주로 사용한다.
- @Query, 값, DTO 조회하기
- 단순히 값 하나를 조회
@Query("select m.username from Member m")
List<String> findUserNameList();
- JPA 값 타입(@Embedded)도 이 방식으로 조회할 수 있다.
- DTO로 직접 조회
@Query("select new study.datajpa.dto.MemberDto(m.id, m.username, t.name) from Member m join m.team t")
List<MemberDto> findMemberDto();
- DTO로 직접 조회 하려면 JPA의 new 명령어를 사용해야 한다.
- 또, 생성자가 맞는 DTO가 필요하다. (JPA와 사용방식이 동일하다.)
- MemberDto
@Data
public class MemberDto {
private Long id;
private String username;
private String teamName;
public MemberDto(Long id, String username, String teamName) {
this.id = id;
this.username = username;
this.teamName = teamName;
}
}
- Test 코드
@Test
public void findMemberDto() {
Team team = new Team("teamA");
teamRepository.save(team);
Member m1 = new Member("AAA", 10);
m1.setTeam(team);
memberRepository.save(m1);
Member m2 = new Member("BBB", 20);
m2.setTeam(team);
memberRepository.save(m2);
List<MemberDto> memberDto = memberRepository.findMemberDto();
for (MemberDto dto : memberDto) {
System.out.println("dto = " + dto);
}
}
- DTO를 직접 조회하는 방식으로 엔티티가 외부에 노출되지 않고 원하는 데이터만 골라서 데이터를 전송할 수 있다.
- 파라미터 바인딩
1. 위치 기반
2. 이름 기반 (권장)
select m from Member m where m.username = :name //이름 기반
- 파라미터 바인딩
@Query("select m from Member m where m.username in :names")
List<Member> findByNames(@Param("names") List<String> names);
- Test 코드
@Test
public void findByNames() {
Member m1 = new Member("AAA", 10);
Member m2 = new Member("BBB", 20);
memberRepository.save(m1);
memberRepository.save(m2);
List<Member> result = memberRepository.findByNames(Arrays.asList("AAA", "BBB"));
for (Member member : result) {
System.out.println("member = " + member);
}
}
- (Arrays.asList를 통해 배열을 List로 변환할 수 있다.)
- @Param과, In 쿼리, List를 이용해서 원하는 조건의 데이터를 쿼리 한번에 조회할 수 있다.
- 반환 타입
- 스프링 데이터 JPA는 유연한 반환 타입을 지원한다.
List<Member> findByUsername(String name); //컬렉션
Member findByUsername(String name); //단건
Optional<Member> findByUsername(String name); //단건 Optional
- 조회 결과가 많거나 없으면?
1. 컬렉션
1.1. 결과 없음: 빈 컬렉션 반환
2. 단건 조회
2.1. 결과 없음: null 반환
2.2. 결과가 2건 이상: javax.persistence.NonUniqueResultException 예외 발생
@Test
public void returnType() {
Member m1 = new Member("AAA", 10);
Member m2 = new Member("BBB", 20);
memberRepository.save(m1);
memberRepository.save(m2);
List<Member> aaa = memberRepository.findListByUsername("AAA");
Member bbb = memberRepository.findMemberByUsername("AAA");
System.out.println("bbb = " + bbb);
Optional<Member> ccc = memberRepository.findOptionalByUsername("AAA");
System.out.println("ccc.get() = " + ccc.get());
List<Member> result = memberRepository.findListByUsername("ASDF");
System.out.println("result.size() = " + result.size());
Member findMember = memberRepository.findMemberByUsername("ASDF");
System.out.println("findMember = " + findMember);
}
}
- 컬렉션 조회할 때 이상한 값을 넣으면 조회 데이터가 없을 수 있다. -> Null로 반환되는 것이 아니라 빈 결과값을 보낸다. (if != null 이런 것 사용하지 않도록 주의해야 한다.)
- 단건 조회는 Null로 반환된다.
- 단건에서 있을수도 있고 없을수도 있으면 Optional을 쓰는 것이 좋다. Null 예외를 Optional에서 처리해주기 때문이다.
- 하지만, 단건 조회에서 여러 데이터가 조회될 경우 예외가 발생한다.
출처 : 인프런 - 김영한(실전! 스프링 데이터 JPA)
'Programming > Spring' 카테고리의 다른 글
Spring Data JPA - (벌크성 수정 쿼리) (0) | 2023.07.26 |
---|---|
Spring Data JPA - 페이징과 정렬 (0) | 2023.07.25 |
Spring Data JPA - 공통 인터페이스 기능 (0) | 2023.07.24 |
Spring - 스프링 트랜잭션 전파2 - 활용 (0) | 2023.07.08 |
Spring - 스프링 트랜잭션 전파1 - 기본 (0) | 2023.07.08 |