Specifications(명세)
스프링 데이터 JPA는 JPA Criteria를 활용해서 이 개념을 사용할 수 있도록 지원한다.
술어(predicate)
- 참 또는 거짓으로 평가한다.
- AND OR 같은 연산자로 조합해서 다양한 검색조건을 쉽게 생성한다. (컴포지트 패턴)
- 스프링 데이터 JPA는 org.springframework.data.jpa.domain.Specification 클래스로 정의한다.
실무에서는 JPA Criteria를 거의 사용하지 않고 QueryDsl을 사용한다고 한다!
Query By Example
@SpringBootTest
@Transactional
public class QueryByExampleTest {
@Autowired MemberRepository memberRepository;
@Autowired EntityManager em;
@Test
public void basic() throws Exception {
//given
Team teamA = new Team("teamA");
em.persist(teamA);
em.persist(new Member("m1", 0, teamA));
em.persist(new Member("m2", 0, teamA));
em.flush();
//when
//Probe 생성
Member member = new Member("m1");
Team team = new Team("teamA"); //내부조인으로 teamA 가능
member.setTeam(team);
//ExampleMatcher 생성, age 프로퍼티는 무시
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("age");
Example<Member> example = Example.of(member, matcher);
List<Member> result = memberRepository.findAll(example);
//then
assertThat(result.size()).isEqualTo(1);
}
}
- Probe: 필드에 데이터가 있는 실제 도메인 객체
- ExampleMatcher: 특정 필드를 일치시키는 상세한 정보 제공, 재사용 가능
- Example: Probe와 ExampleMatcher로 구성, 쿼리를 생성하는데 사용
장점
- 동적 쿼리를 편리하게 처리할 수 있다.
- 도메인 객체를 그대로 사용한다.
- 데이터 저장소를 RDB에서 NOSQL로 변경해도 코드 변경이 없게 추상화 되어 있다.
- 스프링 데이터 JPA JpaRepository 인터페이스에 이미 포함되어 있다.
단점
- 조인은 가능하지만 내부 조인만 가능하고 외부 조인은 안된다.
- 중첩 제약조건도 불가능 할 수 있다.
- 매칭 조건이 매우 단순하다.(문자를 제외한 다른 속성은 = 만 지원한다)
Projections
엔티티 대신에 DTO를 편리하게 조회할 때 사용한다.
인터페이스 기반 Closed Projections
- UsernameOnly 생성
public interface UsernameOnly {
String getUsername();
}
- 조회할 엔티티의 필드를 getter 형식으로 지정하면 해당 필드만 선택해서 조회(Projection)
public interface MemberRepository ... {
List<UsernameOnly> findProjectionsByUsername(String username);
}
인터페이스 기반 Open Projections
public interface UsernameOnly {
@Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
String getUsername();
}
이렇게 SpEL문법을 사용하면, DB에서 엔티티 필드를 다 조회해온 다음에 계산한다. 따라서 JPQL SELECT 절 최적화가 안된다.
클래스 기반 Projection
인터페이스가 아닌 구체적인 DTO 형식도 가능하다
public class UsernameOnlyDto {
private final String username;
public UsernameOnlyDto(String username) { //파라미터 명이 중요하다
this.username = username;
}
public String getUsername() {
return username;
}
}
동적 Projections
Generic type을 주면, 동적으로 프로젝션 데이터 번경 가능하다.
<T> List<T> findProjectionsByUsername(String username, Class<T> type);
//사용 코드
List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1", UsernameOnly.class);
중첩 구조 처리
public interface NestedClosedProjection {
String getUsername();
TeamInfo getTeam();
interface TeamInfo {
String getName();
}
}
이렇게 하면 userName만 가져오고 Team은 전부 다 가져온다. 그 후 member left join team을 한다.
중첩 구조 처리 주의
- 프로젝션 대상이 root 엔티티면, JPQL SELECT 절 최적화 가능
- 프로젝션 대상이 ROOT가 아니면 left outer join 사용, 모든 필드를 select해서 엔티티로 조회한 다음에 계산한다.
네이티브 쿼리
가급적 네이티브 쿼리는 사용하지 않는게 좋음, 정말 어쩔 수 없을 때 사용해야 한다.
스프링 데이터 JPA 기반 네이티브 쿼리
페이징을 지원하고 반환타입은 Object[], Tuple, DTO 이다.
제약은
- Sort 파라미터를 통한 정렬이 정상 동작하지 않을 수 있다.(믿지 말고 직접 처리)
- JPQL처럼 애플리케이션 로딩 시점에 문법 확인 불가능하다.
- 동적 쿼리가 불가능하다.
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query(value = "select * from member where username = ?", nativeQuery = true)
Member findByNativeQuery(String username);
이렇게 할 경우 select *로 쿼리가 나간다.
- JPQL은 위치 기반 파리미터를 1부터 시작하지만 네이티브 SQL은 0부터 시작한다.
- 네이티브 SQL을 엔티티가 아닌 DTO로 변환은 하려면 DTO 대신 JPA TUPLE 조회, DTO 대신 MAP 조회, @SqlResultSetMapping (복잡), Hibernate ResultTransformer를 사용해야함(복잡)
- 네이티브 SQL을 DTO로 조회할 때는 JdbcTemplate or myBatis 권장
Projections 활용
@Query(value = "SELECT m.member_id as id, m.username, t.name as teamName " +
"FROM member m left join team t ON m.team_id = t.team_id",
countQuery = "SELECT count(*) from member",
nativeQuery = true)
Page<MemberProjection> findByNativeProjection(Pageable pageable);
정적 네이티브 쿼리는 이렇게 이름을 맞추는 것으로 페이징 처리를 쉽게 할 수 있다.
단, 동적 네이티브 쿼리는 불가능 하므로 하이버네이트를 직접 활용 하거나, 스프링 JdbcTemplate, myBatis, jooq같은 외부 라이브러리 사용해야 한다.
출처
실전! 스프링 데이터 JPA - 인프런 | 강의
스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제공합니다.
www.inflearn.com
'BackEnd > 스프링 데이터 JPA' 카테고리의 다른 글
스프링 데이터 JPA 분석 (1) | 2023.01.23 |
---|---|
확장 기능 (0) | 2023.01.23 |
@EntityGraph, JPA Hint @ Lock (0) | 2023.01.21 |
벌크성 수정 쿼리 (1) | 2023.01.20 |
JPA 페이징 처리 (0) | 2023.01.14 |