본문 바로가기

기록하기

Day + 32


참고 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/

 

Spring Data JPA - Reference Documentation

Example 121. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io


// 예제

public interface MemberRepository extends JpaRepository<Member, Long> {
	List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}

 

  • Spring Data JPA는 메소드 이름을 분석해서 JPQL을 생성하고 실행
  • 쿼리 메소드 필터 조건 - 공식문서 참고(위에 링크)
  • 제공하는 쿼리 메소드 기능
    • 조회
    • COUNT
    • EXISTS
    • 삭제
    • DISTINCT
    • LIMIT

  • 참고사항
    • 이 기능은 엔티티의 필드명이 변경되면 인터페이스에 정의한 메서드 이름도 꼭 함께 변경해야함
    • 그렇지 않으면 애플리케이션을 시작하는 시점에 오류 발생
    • 이렇게 애플리케이션 로딩 시점에 오류를 인지할 수 있는 것이 스프링 데이터 JPA의 매우 큰 장점

  • JPA NamedQuery -> 잘 사용안함 
// @NamedQuery 어노테이션으로 Named 쿼리 정의

@Entity
@NamedQuery(
	name="Member.findByUsername",
	query="select m from Member m where m.username = :username")
    
public class Member {
... 
}

 

 

// JPA 직접 사용해서 Named 쿼리 호출

public class MemberRepository {
      public List<Member> findByUsername(String username) {
          ...
          List<Member> resultList =
              em.createNamedQuery("Member.findByUsername", Member.class)
                  .setParameter("username", username)
                  .getResultList();
	} 
}

 

// 스프링 데이터 Jpa로 NamedQuery 사용

@Query(name = "Member.findByUsername")
List<Member> findByUsername(@Param("username") String username);

 

// 스프링 데이터 Jpa로 Named 쿼리 호출

public interface MemberRepository
		extends JpaRepository<Member, Long> { //** 여기 선언한 Member 도메인 클래스
    
    List<Member> findByUsername(@Param("username") String username);
  }

 

  • 참고
    • 스프링 데이터 JPA를 사용하면 실무에서 Named Query를 직접 등록해서 사용하는 일은 드물다.
    • 대신 @Query를 사용해서 리파지토리 메소드에 쿼리를 직접 정의 
    • Named Query (가장 큰)장점 -> 애플리케이션 로딩시점에 오류를 잡을 수 있음

@Query, 리포지토리에 쿼리 메소드 정의 하기(많이 씀)

 public interface MemberRepository extends JpaRepository<Member, Long> {
 
    @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);
    
}

 

  • @org.springframework.data.jpa.repository.Query 어노테이션 사용
  • 실행할 메서드에 정적 쿼리를 직접 작성하므로 이름 없는 Named 쿼리라 할 수 있음
  • JPA Named 쿼리처럼 애플리케이션 실행 시점에 문법 오류를 발견할 수 있음 (엄청 큰 장점)
  • 참고
    • 실무에서는 메소드 이름으로 쿼리 생성 기능은 파라미터가 증가하면 메서드 이름이 매우 지저분해짐. 
    • 그래서 @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 사용방식과 동일)

  • 반환타입
List<Member> findByUsername(String name); //컬렉션 
Member findByUsername(String name); //단건
Optional<Member> findByUsername(String name); //단건 Optional

  • 조회 결과가 많거나, 없으면?
    • 컬렉션 : 결과없음, 빈 컬렉션 반환
    • 단건조회
      • 결과 없음 : Null 반환
      • 결과가 2건 이상 : 'javax.persistence.NonUniqueResultException' 예외 발생

// 페이징과 정렬 사용 예제

Page<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 
Slice<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안 함
List<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안 함
List<Member> findByUsername(String name, Sort sort);

// Page 인터페이스

public interface Page<T> extends Slice<T> {
	int getTotalPages(); //전체 페이지 수 longgetTotalElements();//전체 데이터 수
	<U> Page<U> map(Function<? super T, ? extends U> converter); //변환기
}

 

// Slice 인터페이스

public interface Slice<T> extends Streamable<T> {

    intgetNumber(); // 현재 페이지
    intgetSize(); // 페이지 크기
    int getNumberOfElements();  // 현재 페이지에 나올 데이터 수
    List<T> getContent(); // 조회된 데이터
    boolean hasContent(); // 조회된 데이터 존재 여부
    Sort getSort(); // 정렬 정보
    boolean isFirst(); // 현재 페이지가 첫 페이지 인지 여부
    boolean isLast(); // 현재 페이지가 마지막 페이지 인지 여부
    boolean hasNext(); // 다음 페이지 여부
    boolean hasPrevious(); // 이전 페이지 여부
    Pageable getPageable(); // 페이지 요청 정보
    Pageable nextPageable(); // 다음 페이지 객체
    PageablepreviousPageable(); // 이전 페이지 객체 
    <U> Slice<U> map(Function<? super T, ? extends U> converter); // 변환기
    
}

 

'기록하기' 카테고리의 다른 글

Docker 삭제 명령어  (0) 2023.11.16
Amazon java 17 다운  (0) 2023.11.16
Springboot + Docker + Github action  (0) 2023.11.15
Redis (1)  (1) 2023.11.15
Day + 29  (0) 2023.11.13