이거는 보기에 생각보다 좋아보인다.
@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();
Member member = new Member("m1");
Example<Member> example = Example.of(member);
List<Member> result = memberRepository.findAll(example);
assertThat(result.size()).isEqualTo(1);
assertThat(result.get(0).getUsername()).isEqualTo("m1");
}
이렇게. 말 그대로 예시. 도메인 자체로 예시를 만들어서,
저 findAll에 집어넣는다.
Example이 인자로 들어갈 수 있는 이유는
@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
저기 JpaRepository가 상속받은 것 중에
QueryByExampleExecutor<T> 라는 게 있다.
public interface QueryByExampleExecutor<T> {
<S extends T> Optional<S> findOne(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example);
.......
얘네가 다 이런 식으로 정의 해놓음.
근데 저렇게 해서 쿼리 날렸더니
select
m1_0.member_id,
m1_0.age,
m1_0.create_by,
m1_0.created_date,
m1_0.last_modified_by,
m1_0.last_modified_date,
m1_0.team_id,
m1_0.username
from
member m1_0
where
m1_0.age=?
and m1_0.username=?
이렇게 age까지 같이 날라감.
age가 primary 타입이라 무시할 수 없었나 봄. 객체면 그냥 null이라고 무시했을 텐데
0인지 하고 알아서 날라감. 0으로 알아서 넣어졌나봄.
그래서 저런 특정 조건들 제외하고 싶으면
@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();
Member member = new Member("m1");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("age");
Example<Member> example = Example.of(member, matcher);
List<Member> result = memberRepository.findAll(example);
assertThat(result.size()).isEqualTo(1);
assertThat(result.get(0).getUsername()).isEqualTo("m1");
}
이렇게 ExampleMatcher를 만들어, Example 만들때 같이 넣어줌.
그러면
select
m1_0.member_id,
m1_0.age,
m1_0.create_by,
m1_0.created_date,
m1_0.last_modified_by,
m1_0.last_modified_date,
m1_0.team_id,
m1_0.username
from
member m1_0
where
m1_0.username=?
이렇게 제외되서 나감.
matcher는 말 그대로 매칭시켜주는거임.
지금은 withIgnorePaths해서 매치에서 빼는 쿼리를 메소드를 통해 추가시켜 준 거고,
보통
도메인, ExampleMatcher 이걸로 Example을 만드는 데,
ExampleMatcher가 특정 필드와 일치시키는 상세한 정보를 제공함.
저 matcher에 뭐 이것저것 추가시켜 주면 됨. if(~~~) matcher. 이런 식으로..
그리고, join도 되긴함. 근데 하자가 좀 있음
@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();
Member member = new Member("m1");
Team team = new Team("teamA");
member.changeTeam(team);
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("age");
Example<Member> example = Example.of(member, matcher);
List<Member> result = memberRepository.findAll(example);
assertThat(result.size()).isEqualTo(1);
assertThat(result.get(0).getUsername()).isEqualTo("m1");
}
보면 그냥 아예 예시로 만들어버릴 도메인에 연관관계 자체를 넣어주면 됨.
솔직히 되게 좋아보임.
물론 matcher로 제외할 거 짜는 건 좀 귀찮아 보이긴 함. (원시타입은 무시가 안되니.)
근데, 일단 JOIN이 inner조인만 됨. left 조인은 안됨.
그리고 지원하는 매칭 조건이 한계가 있음.
문자는 시작문자열, 끝 문자열, 포함, regex? 뭐 정규표현식 이러는 데, 여튼 문자는 내가 보기에 꽤 괜찮게 지원 하는데,
다른 속성은 = 해서 정확한 매칭만 지원함.
그리고 중첩 제약 조건이 안됨.
뭐 DB에 보면 막 괄호 써서 하는 그런거
firstname = ?0 or (firstname = ?1 and lastname = ?2)
이런 거
일단은 그렇게 오래된 기술은 아니니.. 발전의 여지는 있으나, 지금은 QueryDSL을 쓸 듯
'스프링데이터 + JPA > 스프링 데이터 JPA' 카테고리의 다른 글
29. 네이티브 쿼리 (0) | 2023.11.26 |
---|---|
28. Projections (0) | 2023.11.26 |
26. 명세 (Specifications) (0) | 2023.11.26 |
25. 새로운 엔티티 구별방법, isNew (0) | 2023.11.26 |
24. 스프링 데이터 JPA 구현체 분석 (0) | 2023.11.26 |