La foret rouge
Published on

JPA 공식 문서 (여기저기) 읽기

Authors
  • avatar
    Name
    신주용

Spring Data JPA

핵심 개념

  • Repository는 가장 중심이 되는 인터페이스.
  • Repository에서 관리할 도메인 클래스와 그 클래스의 식별자 타입을 인자로 받음.
    @Entity
    class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    }
    
    interface UserRepository extends JpaRepository<User, Long> {}
    
  • CrudRepository는 Repository를 상속하여 save, findById, findAll 같은 자주 사용되는 메서드를 제공. JpaRepository는 CrudRepository를 더 확장하는 인터페이스.

엔티티 유지(저장)하기

  • CrudRepository.save(...) 메소드를 사용해 엔티티 저장 가능.
    • EntityManager를 통해 엔티티를 유지(persist)하거나 병합(merge)함.
    • persist된 적이 없는 엔티티라면 entityManager.persist(...)로 persist 시키고, 아니면 entityManager.merge(...)를 호출.
  • Entity가 new인지 아닌지를 확인해야 persist(), merge()를 다르게 호출할 수 있음.
    • 기본적으로는 Version-Property가 있는지 검사. 이 값이 null이면 새로 생성된 엔티티로 판단. Version-Property가 없으면 Id-Property를 검사하는데 이 값도 null이면 새 엔티티로 판단.

쿼리 메서드 정의

  • Repository proxy는 '메서드명을 통해 생성' 또는 '직접 작성한 쿼리 사용' 방법으로 쿼리를 도출함.
  • 기본값은 CREATE_IF_NOT_FOUND
    • CREATE와 USE_DECLARED_QUERY 섞은 방법.
    • 직접 작성한 쿼리가 있다면 그 쿼리를 사용하고, 없다면 메서드명을 기반으로 쿼리를 생성.

메서드명을 파싱해 쿼리를 만드는 과정

  • find...By, exists...By처럼 By 키워드를 전후로 Subject, 그 외 나머지는 Predicate로 구분.
    • Subject 부분은 키워드 목록에 있는 단어 외에는 모두 단순 설명으로 처리됨. (쿼리 생성에 영향을 주지 않음)
  • predicate는 AND, OR를 사용해 여러 프로퍼티를 연결해서 사용 가능.

프로퍼티 표현식

  • 프로퍼티 표현식은 기본적으로는 리포지토리가 직접 관리하는 엔티티의 프로퍼티만 참조할 수 있음. 프로퍼티의 타입이 클래스라면 중첩된 프로퍼티도 탐색 가능.
    • CamelCase 형태로 분할하며 프로퍼티를 탐색하는데, 잘못된 프로퍼티를 선택할 수도 있으므로 _를 사용해 구분 가능.

예시

class Email {
    private String account;
    private String domain;
}

@Entity
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String firstname;
    private String lastname;

    private List<Email> emails;
}

interface UserRepository extends JpaRepository<User, Long> {

    Optional<User> findById(Long id);           // (1)
    boolean existsByLastname(String lastname);  // (2)
    List<User> findByLastname(String lastname); // (3)
    List<User> findByLastnameOrderByFirstnameDesc(String lastname); // (4)
    List<User> findByEmails_Domain(String domain); // (5)
    Page<User> findByLastname(String lastname, Pageable pageable); // (6)

}
  1. 아이디로 유저 조회 (존재하지 않을 수 있음)
  2. 특정 lastname을 가진 유저의 존재 여부 조회
  3. lastname으로 유저 목록 조회
  4. lastname으로 유저 목록을 조회한 결과를 firstname 내림차순으로 정렬
  5. 특정 이메일(ex. 'gmail.com')을 사용하는 유저 조회
    • 이 repository가 User 타입을 관리하므로 findBy 뒤에는 User의 필드인 Emails가 옴
    • 이 때 타입은 Email이므로 _ 뒤에는 Email 객체의 필드인 Domain을 사용
    • _는 필수는 아니지만 EmailsDomain이라는 이름을 가진 필드가 만약 존재한다면 결과에 혼동을 줄 수 있기 때문에 구분자로 사용
  6. (2)와 동일한 조회이나, 결과를 페이지로 반환