반응형
해당 글은 김영한 님의 querydsl을 수강하며 정리하려고 적는 포스팅입니다.
스프링 데이터 JPA 리포지토리로 변경
스프링 데이터 JPA - MemberRepository 생성
public interface MemberRepository extends JpaRepository<Member, Long> {
//select m from Member m where m.username =:?
List<Member> findByUsername(String username);
}
- 기본 CRDU(정적쿼리) → Spring Data JPA가 interface만 만들면 이름으로 매칭해서 자동으로 구현체를 매칭
스프링 데이터 JPA 테스트
@SpringBootTest
@Transactional
class MemberRepositoryTest {
@PersistenceContext
EntityManager em;
@Autowired
MemberRepository memberRepository;
@Test
public void basicTest() {
Member member = new Member("member1", 10);
memberRepository.save(member);
Member findMember = memberRepository.findById(member.getId()).get();
assertThat(findMember).isEqualTo(member);
List<Member> result1 = memberRepository.findAll();
assertThat(result1).containsExactly(member);
List<Member> result2 = memberRepository.findByUsername("member1");
assertThat(result2).containsExactly(member);
}
}
------------------------------------------------------------------------------
2023-01-18T09:02:22.551+09:00 DEBUG 16408 --- [ main] org.hibernate.SQL :
/* <criteria> */ select
m1_0.member_id,
m1_0.age,
m1_0.team_id,
m1_0.username
from
member m1_0
2023-01-18T09:02:22.597+09:00 DEBUG 16408 --- [ main] org.hibernate.SQL :
/* <criteria> */ select
m1_0.member_id,
m1_0.age,
m1_0.team_id,
m1_0.username
from
member m1_0
where
m1_0.username=?
사용자 정의 Repository
사용자 정의 인터페이스 작성
public interface MemberRepositoryCustom {
List<MemberTeamDto> search(MemberSearchCondition condition);
}
사용자 정의 인터페이스 구현
public class MemberRepositoryImpl implements MemberRepositoryCustom{
private final JPAQueryFactory queryFactory;
public MemberRepositoryImpl(JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
@Override
public List<MemberTeamDto> search(MemberSearchCondition condition) {
return queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
team.id.as("teamId"),
team.name.as("teamName")
))
.from(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe()))
.fetch();
}
private BooleanExpression usernameEq(String username) {
return hasText(username) ? member.username.eq(username) : null;
}
private BooleanExpression teamNameEq(String teamName) {
return hasText(teamName) ? team.name.eq(teamName) : null;
}
private BooleanExpression ageGoe(Integer ageGoe) {
return ageGoe != null ? member.age.goe(ageGoe) : null;
}
private BooleanExpression ageLoe(Integer ageLoe) {
return ageLoe != null ? member.age.loe(ageLoe) : null;
}
}
MemberRepository + Impl : 명명규칙
스프링 데이터 리포지토리에 사용자 인터페이스 상속
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom{
//select m from Member m where m.username =:?
List<Member> findByUsername(String username);
}
- interface는 여러 개 상속받을 수 있다.
커스텀 리포지토리 동작 테스트
@SpringBootTest
@Transactional
class MemberRepositoryTest {
@PersistenceContext
EntityManager em;
@Autowired
MemberRepository memberRepository;
@Test
public void searchTest() {
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
em.persist(teamA);
em.persist(teamB);
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 20, teamA);
Member member3 = new Member("member3", 30, teamB);
Member member4 = new Member("member4", 40, teamB);
em.persist(member1);
em.persist(member2);
em.persist(member3);
em.persist(member4);
MemberSearchCondition condition = new MemberSearchCondition();
condition.setAgeGoe(35);
condition.setAgeLoe(40);
condition.setTeamName("teamB");
List<MemberTeamDto> result = memberRepository.search(condition);
assertThat(result).extracting("username").containsExactly("member4");
}
}
-------------------------------------------------------------------------------------
2023-01-18T09:29:20.934+09:00 DEBUG 9048 --- [ main] org.hibernate.SQL :
/* select
member1.id as memberId,
member1.username,
member1.age,
team.id as teamId,
team.name as teamName
from
Member member1
left join
member1.team as team
where
team.name = ?1
and member1.age >= ?2
and member1.age <= ?3 */ select
m1_0.member_id,
m1_0.username,
m1_0.age,
m1_0.team_id,
t1_0.name
from
member m1_0
left join
team t1_0
on t1_0.id=m1_0.team_id
where
t1_0.name=?
and m1_0.age>=?
and m1_0.age<=?
참고
만약 Search가 쿼리가 복잡하고 특정 화면이나, API에 특화된 것이라고 가정하자.
그렇다면 따로 MemberQueryRepository와 같은 class로 생성하고 injection 받아서 사용하면 된다.
공용성이 없고, 특정 API에 종속되어 있으면 수정 라이프 사이클 자체가 API와 화면에 맞추어져서 기능이 변경된다. 그래서 찾기도 편하고 수정도 편하게 된다. 따라서 별도로 조회용 repository를 만드는 것도 괜찮다. 너무 Custom에 억압 받지 않아도 된다.
기본은 Custom을 사용하고, 아키텍처에 맞게 분리해서 사용하는 것도 괜찮은 방법이라고 한다.
반응형
'Java' 카테고리의 다른 글
QuerydslPredicateExecutor - querydsl 조건 조회 간단히 사용하기 (0) | 2023.01.18 |
---|---|
Querydsl 페이징 연동 및 최적화 - Querydsl fetchResults() , fetchCount() Deprecated(향후 미지원) (0) | 2023.01.18 |
[spring] 환경에 따른 설정 파일 나누기 - application.yml/@Profile (0) | 2023.01.17 |
[Querydsl] 동적 쿼리와 성능 최적화 조회 - where 조건 절 파라미터 (0) | 2023.01.17 |
[Querydsl] 동적 쿼리 성능 최적화 조회 - DTO, Builder 사용 (0) | 2023.01.16 |