1. 사용자 정의 리포지토리
- Repository의 메소드를 직접 구현해야 할 경우에는 사용자 정의 리포지토리를 추가하면 된다.
- 아래 3가지 절차를 걸쳐서 적용하면 된다.
i) 사용자 정의 리포지토리 생성
public interface CustomMemberRepository {
// 메소드 정의
}
ii) 구현체 생성
- 사용자 정의 리포지토리 이름 뒤에 Impl가 붙은 구현체를 생성한다. Impl를 붙이지 않으면 Spring Data JPA가 인식하지 못한다.
public class CustomMemberRepositoryImpl implements CustomMemberRepository {
// 메소드 구현
}
- 만약 Impl가 아닌 다른 키워드를 사용하고 싶다면 @Configuration 클래스에서 @EnableJpaRepositories.repositoryImplementationPostfix 속성에 원하는 키워드를 지정하면 된다.
iii) 사용자 정의 리포지토리 상속
public interface MemberRepository extends JpaRepository<Member, Long>, CustomMemberRepository {...}
2. @EnableSpringDataWebSupport
- Spring MVC에서 사용할 수 있는 편리한 기능을 제공해주는 어노테이션으로, @Configuration 클래스에 붙이면 적용된다.
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
public class AppConfig {...}
1) 도메인 클래스 컨버터 기능
- 내부에 등록된 DomainClassConverter의 기능이다.
- URI로 들어온 식별자 파라미터를 통해 바로 엔티티를 반환해주는 기능이다.
@RequestMapping("/member")
public String member(@RequestParam("id") Member member, Model model) {...}
2) 페이징과 정렬 기능
- 내부에 등록된 HandlerMethodArgumentResolver의 기능이다.
- 컨트롤러 메소드의 파라미터로 Pageable을 받는다. Pageable은 page, size, sort 파라미터 정보로 만들어진다.
@RequestMapping("/members")
public String members(Pageable pageable, Model model) {...}
// 요청 예시 -> /members?page=4&size=25&sort=name,desc&sort=address.city
※ 기본값은 page=0, size=20이다. 기본값을 변경하고 싶다면 Pageable 앞에 @PageableDefault 어노테이션을 추가해서 지정하자.
※ 페이지는 기본적으로 0부터 시작된다. 페이지를 1부터 시작하고 싶다면 PageableHandlerMethodArgumentResolver를 bean으로 등록하고 setOneIndexedParameters를 true로 설정하면 된다.
- 사용해야 할 페이징 정보가 여러 개라면 @Qualifier 어노테이션으로 접두사를 만들어서 사용하면 된다. 요청 파라미터의 이름 앞에 "[접두사]_"를 붙여 구분하면 된다.
※ 예) member_page=3
@RequestMapping("/list")
public String list(
@Qualifier("member") Pageable memberPageable,
@Qualifier("team") Pageable teamPageable,
Model model
) {...}
3. Spring Data JPA가 생성하는 리포지토리 구현체의 특징
- 등록/수정/삭제 메소드에 @Transactional이 적용되고, 조회 메소드에는 @Transactional(readOnly=true)이 적용된다.
- save() 메소드는 저장할 엔티티가 새로운 엔티티면 EntityManager.persist()를 호출하고 이미 있는 엔티티면 EntityManager.merge()를 호출한다. 새로운 엔티티라고 판단하는 기준은 엔티티의 식별자 값이 null이거나 0일 때다.
※ Persistable 인터페이스를 구현해서 새로운 엔티티를 판단 하는 로직을 변경할 수 있다.
4. Spring Data JPA와 QueryDSL 통합
- Spring Data JPA에서 QueryDSL을 지원하는 두 가지 방법을 살펴보자.
1) QueryDslPredicateExecutor
- 리포지토리에서 QueryDslPredicateExecutor 인터페이스를 상속받아서 사용할 수 있다.
public interface ItemRepository extends JpaRepository<Item, Long> QueryDslPredicateExecutor<Item> {...}
- QueryDslPredicateExecutor는 인자로 Predicate 객체가 들어가는 findAll(), findBy(), findOne(), count(), exists() 메소드를 지원한다.
※ QueryDslPredicateExecutor의 메소드들 : https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/querydsl/QuerydslPredicateExecutor.html
사용 예시
Qitem qitem = Qitem.item;
Iterable<Item> result = itemRepository.findAll(
qitem.name.contains("LG").and(qitem.price.between(800000, 1300000);
);
- 문서에 나와 있듯이, findAll() 메소드에 Pageable이나 Sort 객체를 파라미터에 추가해서 페이징과 정렬 기능을 사용할 수도 있다.
- join, fetch 키워드를 사용할 수 없다는 단점이 있다.
2) QueryDslRepositorySupport
- JPQLQuery 객체를 반환하는 from() 메소드를 이용할 수 있게 되면서 QueryDsl의 모든 기능을 사용할 수 있게 되었다.
- QueryDslRepositorySupport의 메소드들 : https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.html
사용 예시
public class OrderRepositoryImpl extends QueryDslRepositorySupport implements CustomOrderRepository {
public OrderRepositoryImpl() {
super(Order.class);
}
@Override
public List<Order> search(OrderSearch orderSearch) {
QOrder qorder = QOrder.order;
QMember qmember = QMember.member;
JPQLQuery query = from(qorder);
if(StringUtils.hasText(orderSearch.getMemberName())) {
query.leftJoin(qorder.member, qmember)
.where(qmember.name.contains(orderSearch.getMemberName()));
}
if (orderSearch.getOrderStatus() != null) {
query.where(qorder.status.eq(orderSearch.getOrderStatus()));
}
return query.list(qorder);
}
}
● 참고자료 : 자바 ORM 표준 JPA 프로그래밍
'Spring 사전 준비 > JPA Hibernate' 카테고리의 다른 글
[JPA] JPA와 컬렉션 (0) | 2022.08.24 |
---|---|
[JPA] 웹 애플리케이션에서의 영속성 관리 (0) | 2022.08.22 |
[JPA] Spring Data JPA(1) (0) | 2022.08.19 |
[JPA] JPQL 최적화 (0) | 2022.08.14 |
[JPA] 스토어드 프로시저 (0) | 2022.08.14 |
댓글