반응형
Spring Data JPA 게시글은 대부분 인프런의 김영한님의 강의인 '실전! 스프링 데이터 JPA' 기반으로 내용을 정리했습니다.
Query By Example
Query By Example은 쿼리를 Example에 의해서 하겠다는 기능이다. 코드로 살펴보자.
@Test
public void queryByExample() {
Team teamA = new Team("teamA");
em.persist(teamA);
Member m1 = new Member("m1", 0, teamA);
Member m2 = new Member("m2", 0, teamA);
em.persist(m1);
em.persist(m2);
em.flush();
em.clear();
//Probe
Member member = new Member("m1"); // 엔티티 자체가 검색 조건이 된다.
Example<Member> ex = Example.of(member);
List<Member> result = memberRepository.findAll(ex);
assertThat(result.get(0).getUsername()).isEqualTo("m1");
}
select
member0_.member_id as member_i1_1_,
member0_.create_by as create_b2_1_,
member0_.create_date as create_d3_1_,
member0_.last_modified_by as last_mod4_1_,
member0_.last_modified_date as last_mod5_1_,
member0_.age as age6_1_,
member0_.team_id as team_id8_1_,
member0_.username as username7_1_
from
member member0_
where
member0_.username=?
and member0_.age=0 //**
Member 도메인 객체(Probe)에 m1(username)을 넣어주고 그 자체를 검색 조건으로 사용한다. 엔티티를 검색 조건으로 사용해서 쿼리를 날려보니 뭔가 이상한 점이 있다. age가 조건에 0으로 추가되어서 select 쿼리가 발생했다..! 이유는 Member 클래스 필드에 id, username, age가 있기 때문이다. id는 Long이라 null이 들어가 무시를 해버리지만 age는 primitive type인 int이기 때문에 null이 못 들어가 0이 들어가버려 무시를 하지 못한다. 그래서 따로 처리를 해줘야 한다.
Member member = new Member("m1");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("age"); //** 추가
Example<Member> ex = Example.of(member,matcher);
List<Member> result = memberRepository.findAll(ex);
assertThat(result.get(0).getUsername()).isEqualTo("m1");
ExampleMatcher로 matching().withIgnorePaths()에 age를 추가해주면 age를 무시한다.
select
member0_.member_id as member_i1_1_,
member0_.create_by as create_b2_1_,
member0_.create_date as create_d3_1_,
member0_.last_modified_by as last_mod4_1_,
member0_.last_modified_date as last_mod5_1_,
member0_.age as age6_1_,
member0_.team_id as team_id8_1_,
member0_.username as username7_1_
from
member member0_
where
member0_.username=?
이렇게 보면 좋은 기능일 수 있는데 이 기능의 최대 단점은 inner join만 되고 outer join이 불가능하다는 점이다. inner join은 다음과 같이 검색 조건이 될 Team의 엔티티를 만들고 setTeam()으로 member와 team을 연관 관계가 맺어질 수 있게 해주면 된다.
Member member = new Member("m1"); // 엔티티 자체가 검색 조건이 된다.
Team team = new Team("teamA");
member.setTeam(team);
select
member0_.member_id as member_i1_1_,
member0_.create_by as create_b2_1_,
member0_.create_date as create_d3_1_,
member0_.last_modified_by as last_mod4_1_,
member0_.last_modified_date as last_mod5_1_,
member0_.age as age6_1_,
member0_.team_id as team_id8_1_,
member0_.username as username7_1_
from
member member0_
inner join
team team1_
on member0_.team_id=team1_.team_id
where
member0_.username=?
and team1_.name=?
장점
- 동적 쿼리를 편리하게 처리한다.
- 도메인 객체를 그대로 사용한다.
- 데이터 저장소를 RDB에서 NOSQL로 변경해도 코드 변경이 없게 추상화 되어 있다.
- 스프링 데이터 JPA JpaRepository 인터페이스에 이미 포함되어 있다.
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>
, QueryByExampleExecutor<T> {
단점
- 조인은 가능하지만 내부 조인(INNER JOIN)만 가능하다. 외부 조인(LEFT JOIN)을 지원하지 않는다.
- 다음과 같은 중첩 제약조건이 안 된다.
- firstname = ?0 or (firstname =?1 and lastname = ?2)
- 매칭 조건이 매우 단순하다.
- 문자는 starts, contains, ends, regex
- 다른 속성은 정확한 매칭 = 만 지원한다.
정리
- 실무에서 사용하기에는 매칭 조건이 너무 단순하고, LEFT 조인을 지원하지 않는다.
- 실무에서는 QueryDSL을 사용하자!
반응형