Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- jpa에러
- 빈생성안됨
- git
- 인텔리제이
- 복합키
- Filter
- github
- ERD 작성
- JPA
- 최종 프로젝트
- 스프링부트오류
- spring서버
- REST란
- Q 클래스
- 스프링 부트 공식 문서
- uncheck Exception
- JoinColumn
- queryDSL
- Unsatisfied dependency
- json
- REST API 규칙
- jpa회원가입
- 스프링 부트 기능
- @IdClass
- 1차캐시
- Error creating bean with name
- jwt메서드
- JPA주의사항
- Spring Spring boot 차이
- json gson 차이
Archives
- Today
- Total
Everyday Dev System
QueryDSL의 JPAQueryFactory를 활용한 조회문 본문
JPAQueryFactory을 빈으로 등록하기 위해 configuration 클래스를 생성한다.
package me.chaeyoung.jpa.configuration;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JPAConfiguration {
@PersistenceContext
EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory(){
return new JPAQueryFactory(entityManager);
}
}
먼저 repository 인터페이스 코드이다.
0. ThreadRepository
package me.chaeyoung.jpa.thread;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.RepositoryDefinition;
@RepositoryDefinition(domainClass = Thread.class, idClass = Long.class)
public interface ThreadRepository extends JpaRepository<Thread, Long>
, QuerydslPredicateExecutor<Thread>, ThreadRepositoryQuery {
}
1. ThreadRepositoryQuery
package me.chaeyoung.jpa.thread;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
public interface ThreadRepositoryQuery {
Page<Thread> search(ThreadSearchCond cond, Pageable pageable);
}
조회 조건에 필요한 필드를 묶어놓은 클래스이다.
2. ThreadSearchCond
package me.chaeyoung.jpa.thread;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ThreadSearchCond {
// 조건에 필요한 필드들을 모아놓음.
private Long channelId; // 채널 Id
private Long mentionedUserId; // 멘션된 user_id
}
1번의 인터페이스를 구현한 구현체이다.
3. ThreadRepositoryQueryImpl
package me.chaeyoung.jpa.thread;
import static me.chaeyoung.jpa.channel.QChannel.channel;
import static me.chaeyoung.jpa.thread.QThread.thread;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Wildcard;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
@RequiredArgsConstructor
public class ThreadRepositoryQueryImpl implements ThreadRepositoryQuery {
private final JPAQueryFactory jpaQueryFactory;
@Override
public Page<Thread> search(ThreadSearchCond cond, Pageable pageable) {
var query = query(thread, cond)
.offset(pageable.getOffset())
.limit(pageable.getPageSize());
// Wildcard.count 는 count 쿼리를 날린다는 의미
var countQuery = query(Wildcard.count, cond);
// 실제로 받아올 스레드는 query()를 fetch()만 하면 됨.
var threads = query.fetch();
long totalSize = countQuery.fetch().get(0);
// PageableExecutionUtils.getPage() 메서드를 통해서 threads 를 페이징 객체로 감싸서 반환
return PageableExecutionUtils.getPage(threads, pageable, () -> totalSize);
}
private <T> JPAQuery<T> query(Expression<T> expr, ThreadSearchCond cond) {
// jpaQueryFactory.select() 메서드와 형태 동일하게 구성함.
return jpaQueryFactory.select(expr)
.from(thread)
.leftJoin(thread.channel, channel).fetchJoin()
// fetchJoin() 으로 해야 LAZY 로 설정된 애를 조회해 옴.
.where(
// getChannelId 과 getMentionedUserId 가 null 이면 에러 발생
this.channelIdEq(cond.getChannelId()),
this.mentionedUserIdEq(cond.getMentionedUserId())
);
}
// query() 메서드에 getChannelId 과 getMentionedUserId 가 null 이면 에러 발생
// 하므로 이를 체크해주는 메서드임
// eq() 메서드는 반환값이 BooleanExpression 임.
private BooleanExpression channelIdEq(Long channelId) {
// channelId가 null 이 아니라면 전자 실행 후 반환 , null 이면 null 을 반환
return Objects.nonNull(channelId) ? thread.channel.id.eq(channelId) : null;
}
private BooleanExpression mentionedUserIdEq(Long mentionedUserId) {
return Objects.nonNull(mentionedUserId) ? thread.mentions.any().user.id.eq(mentionedUserId)
: null;
}
}
페이징을 위한 DTO 클래스
4. PageDTO
package me.chaeyoung.jpa.common;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@RequiredArgsConstructor
@AllArgsConstructor
public class PageDTO {
private final Integer currentPage;
private final Integer size;
private String sortBy;
public Pageable toPageable() {
return PageRequest.of(currentPage - 1, size, Sort.by(sortBy).descending());
// 한 페이지에 (size)개씩
}
}
서비스 인터페이스
5. ThreadService
package me.chaeyoung.jpa.thread;
import java.util.List;
import me.chaeyoung.jpa.channel.Channel;
import me.chaeyoung.jpa.common.PageDTO;
import org.springframework.data.domain.Page;
public interface ThreadService {
List<Thread> selectNotEmptyThreadList(Channel channel);
Page<Thread> selectMentionedThreadList(Long userId, PageDTO pageDTO);
Thread insert(Thread thread);
}
서비스 인터페이스를 구현한 클래스
6. ThreadServiceImpl
package me.chaeyoung.jpa.thread;
import com.mysema.commons.lang.IteratorAdapter;
import java.util.List;
import lombok.RequiredArgsConstructor;
import me.chaeyoung.jpa.channel.Channel;
import me.chaeyoung.jpa.common.PageDTO;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ThreadServiceImpl implements ThreadService {
private final ThreadRepository threadRepository;
private final ThreadRepositoryQuery threadRepositoryQuery;
@Override
public Page<Thread> selectMentionedThreadList(Long userId, PageDTO pageDTO) {
var cond = ThreadSearchCond.builder().mentionedUserId(userId).build();
// 페이징을 할 때, PageRequest 로 요청 받아서 응답은 Pageable 타입으로 반환해야 하므로 -> pageDTO.toPageable()
return threadRepositoryQuery.search(cond, pageDTO.toPageable());
}
@Override
public List<Thread> selectNotEmptyThreadList(Channel channel) {
var thread = QThread.thread;
// 메세지가 비어있지 않은 해당 채널의 스레드 목록
var predicate = thread.channel.eq(channel).and(thread.message.isNotEmpty());
var threads = threadRepository.findAll(predicate);
// predicate 조건에 따른 스레드를 findall() 해와라. 그리고 반환하라
return IteratorAdapter.asList(threads.iterator());
}
@Override
public Thread insert(Thread thread) {
return threadRepository.save(thread);
}
}
'내배캠 주요 학습 > JPA 심화' 카테고리의 다른 글
@Transaction propagation (0) | 2023.08.04 |
---|---|
JPA 쿼리 최적화 (0) | 2023.08.04 |
JPA 활용 장점 (0) | 2023.08.01 |
Auditing 활용하기 (1) | 2023.08.01 |
QueryDSL 활용 코드 (0) | 2023.08.01 |