순수 JPA 리포지토리와 Querydsl
이제 순수 JPA 리포지토리에서 Querydsl을 활용해보자
@Repository
public class MemberJPARepository {
private final EntityManager em;
private final JPAQueryFactory queryFactory;
public MemberJPARepository(EntityManager em) {
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
우선 위와 같이 순수 JPA 리포지토리를 만들고
여러 기본 메소드들을 만들어주자
public void save(Member member){
em.persist(member);
}
public Optional<Member> findById(Long id){
Member findMember = em.find(Member.class, id);
return Optional.ofNullable(findMember);
}
public List<Member> findAll(){
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public List<Member> findByUsername(String username){
return em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username",username)
.getResultList();
}
이제 테스트 코드를 작성해 기능이 제대로 작동하는지 확인해 보자
@Test
public void basicTest(){
Member member = new Member("member1", 10);
memberJPARepository.save(member);
Member member1 = memberJPARepository.findById(member.getId()).get();
Assertions.assertThat(member1).isEqualTo(member);
List<Member> all = memberJPARepository.findAll();
Assertions.assertThat(all).containsExactly(member);
}
잘 통과를 했고 이제 메소드들을 하나씩 querydsl로 바꿔보자
Querydsl로 바꾸기
findAll 메소드
public List<Member> findAll_Querydsl() {
return queryFactory
.selectFrom(member)
.fetch();
}
find All 메소드를 이렇게 간단하게 바꿀 수 있다.
findByUsername 메소드
public List<Member> findByUsername(String username){
return em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username",username)
.getResultList();
}
findByUsername 메소드도 바꿔보자
public List<Member> findByUsername_Querydsl(String username){
return queryFactory
.selectFrom(member)
.where(member.username.eq(username))
.fetch();
}
확실히 코드를 알아보기 편하고
자바 코드로 작성하니 오타로 인한 에러를 컴파일 시점에 잡을 수 있다.
이제 테스트 코드를 다시 작성해보자
Querydsl 테스트 코드
@Test
public void basicTest(){
Member member = new Member("member1", 10);
memberJPARepository.save(member);
Member member1 = memberJPARepository.findById(member.getId()).get();
Assertions.assertThat(member1).isEqualTo(member);
List<Member> all = memberJPARepository.findAll_Querydsl();
Assertions.assertThat(all).containsExactly(member);
List<Member> result = memberJPARepository.findByUsername_Querydsl("member1");
Assertions.assertThat(all).containsExactly(member);
}
이렇게 메소드 명만 바꿔주고 테스트를 실행해보자
잘 통과했다.
사소한 팁
public MemberJPARepository(EntityManager em) {
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
쿼리 팩토리를 주입할 때 생성자로 넣어도 되지만 bean으로 등록해서 그냥 주입해도 편리하다.
@SpringBootApplication
public class QuerydslApplication {
public static void main(String[] args) {
SpringApplication.run(QuerydslApplication.class, args);
}
@Bean
JPAQueryFactory jpaQueryFactory(EntityManager em){
return new JPAQueryFactory(em);
}
}
이런 식으로 말이다. 물론 빈은 다른 곳에서 등록시켜도 된다.
public MemberJPARepository(EntityManager em, JPAQueryFactory queryFactory) {
this.em = em;
this.queryFactory = queryFactory;
}
이제 이렇게 변경 가능하다.
스프링이 bean으로 em과 queryfactory를 다 관리해주니
생성자를 생략하고 RequiredArgsConstructor를 사용할 수도 있다.
Dto를 사용해서 동적쿼리 값 반환하기
@Data
public class MemberTeamDto {
private Long memberId;
private String username;
private int age;
private Long teamId;
private String teamName;
@QueryProjection
public MemberTeamDto(Long memberId, String username, int age, Long teamId, String teamName) {
this.memberId = memberId;
this.username = username;
this.age = age;
this.teamId = teamId;
this.teamName = teamName;
}
}
우선 위와 같이 dto를 만들어 주자,
Qtype을 만들기 위해 @QueryProjection을 작성하자.
메소드 작성
public List<MemberTeamDto> searchByBuilder(MemberSearchCondition condition){
BooleanBuilder builder = new BooleanBuilder();
if(StringUtils.hasText(condition.getUsername())){
builder.and(member.username.eq(condition.getUsername()));
}
if(StringUtils.hasText(condition.getTeamName())){
builder.and(team.name.eq(condition.getTeamName()));
}
if(condition.getAgeGoe() != null){
builder.and(member.age.goe(condition.getAgeGoe()));
}
if(condition.getAgeLoe() != null){
builder.and(member.age.loe(condition.getAgeLoe()));
}
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(builder)
.fetch();
}
불리언 빌더를 사용하여 조건을 적용한 걸 먼저 보자
BooleanBuilder builder = new BooleanBuilder();
if(StringUtils.hasText(condition.getUsername())){
builder.and(member.username.eq(condition.getUsername()));
}
if(StringUtils.hasText(condition.getTeamName())){
builder.and(team.name.eq(condition.getTeamName()));
}
if(condition.getAgeGoe() != null){
builder.and(member.age.goe(condition.getAgeGoe()));
}
if(condition.getAgeLoe() != null){
builder.and(member.age.loe(condition.getAgeLoe()));
}
위와 같이 특정 조건에 부합하면 빌더에 조건을 추가하여 동적으로 조건을 추가할 수 있다.
StirngUtils.hasText는 말 그대로 null과 "" 인 값을 걸러주는 기능이다.
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
team.id.as("teamId"),
team.name.as("teamName")
))
셀렉트 절에 dto를 삽입할 때 반드시 컬럼명을 일치시켜줘야 한다.
member 엔티티는 id 이기 때문에 as 를 사용하여 dto의 이름과 동일하게 변경해주었다.
밑에 team id와 team name도 마찬가지다.
주의할 점
만약 빌더에 아무런 조건이 들어있지 않다면, 데이터베이스에 있는 모든 데이터를 다 가져오기 때문에
페이징 처리를 해주면 좋다.
'Tools & Libraries > Querydsl' 카테고리의 다른 글
[Querydsl] Querydsl 중급문법 4 (SQL function 호출하는 방법) (1) | 2024.09.13 |
---|---|
[Querydsl] Querydsl 중급문법 3 (동적 쿼리 - where절 이용, 벌크 연산) (0) | 2024.09.13 |
[Querydsl] Querydsl 중급문법 2 (@QueryProjection, 동적쿼리 - BooleanBuilder) (0) | 2024.09.11 |
[Querydsl] Querydsl 중급문법 1 (프로젝션과 결과 반환 - Dto) (0) | 2024.09.11 |
[Querydsl] Querydsl 기본문법 5 (Case 문, 상수 문자 더하기) (0) | 2024.09.06 |