- Published on
JPA 공식 문서 (여기저기) 읽기
- Authors
- 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
이면 새 엔티티로 판단.
- 기본적으로는 Version-Property가 있는지 검사. 이 값이
쿼리 메서드 정의
- Repository proxy는 '메서드명을 통해 생성' 또는 '직접 작성한 쿼리 사용' 방법으로 쿼리를 도출함.
- 기본값은
CREATE_IF_NOT_FOUND
- CREATE와 USE_DECLARED_QUERY 섞은 방법.
- 직접 작성한 쿼리가 있다면 그 쿼리를 사용하고, 없다면 메서드명을 기반으로 쿼리를 생성.
메서드명을 파싱해 쿼리를 만드는 과정
find...By
,exists...By
처럼By
키워드를 전후로 Subject, 그 외 나머지는 Predicate로 구분.- Subject 부분은 키워드 목록에 있는 단어 외에는 모두 단순 설명으로 처리됨. (쿼리 생성에 영향을 주지 않음)
- predicate는
AND
,OR
를 사용해 여러 프로퍼티를 연결해서 사용 가능.- 키워드 목록 참고.
프로퍼티 표현식
- 프로퍼티 표현식은 기본적으로는 리포지토리가 직접 관리하는 엔티티의 프로퍼티만 참조할 수 있음. 프로퍼티의 타입이 클래스라면 중첩된 프로퍼티도 탐색 가능.
- CamelCase 형태로 분할하며 프로퍼티를 탐색하는데, 잘못된 프로퍼티를 선택할 수도 있으므로
_
를 사용해 구분 가능.
- 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)
}
- 아이디로 유저 조회 (존재하지 않을 수 있음)
- 특정 lastname을 가진 유저의 존재 여부 조회
- lastname으로 유저 목록 조회
- lastname으로 유저 목록을 조회한 결과를 firstname 내림차순으로 정렬
- 특정 이메일(ex. 'gmail.com')을 사용하는 유저 조회
- 이 repository가
User
타입을 관리하므로 findBy 뒤에는 User의 필드인Emails
가 옴 - 이 때 타입은 Email이므로
_
뒤에는Email
객체의 필드인Domain
을 사용 _
는 필수는 아니지만EmailsDomain
이라는 이름을 가진 필드가 만약 존재한다면 결과에 혼동을 줄 수 있기 때문에 구분자로 사용
- 이 repository가
- (2)와 동일한 조회이나, 결과를 페이지로 반환