삽질한 기록은 아래!
Left outer Join , on() 으로 추가 조건을 내새울 수도 있다.
@Override
public List<Post> findMyPostByIdWithReplies(Long id) {
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
List<Post> fetch = queryFactory.select(post)
.from(post)
.leftJoin(post.replies, reply).fetchJoin().fetch();
return fetch;
}
테스트 코드
@Test
@Transactional
@Rollback(value = false)
public void joinTest(){
Post post = postRepository.findById(1L).get();
List<Reply> replies =new ArrayList<>();
for(int i=0; i<5; i++){
Reply reply = new Reply();
reply.setAuthor("Jung " +i);
reply.setAccountId(1L);
reply.setSubject("Reply Subject " + i);
reply.setContent("Reply Content "+i);
reply.setPost(post);
replies.add(reply);
}
replyRepository.saveAll(replies);
List<Post> myPostByIdWithReplies = postRepository.findMyPostByIdWithReplies(1L);
myPostByIdWithReplies.get(0).getReplies().forEach(reply -> System.out.println(reply.getId()));
}
하나의 글에 따른 페이지 네이션 정보를 가져오고 싶을때, 가져오고 싶지않다면 post 속성은 빼주자.
@Override
public Page<Reply> findByPostIdWithPagination(Long id, Pageable pageable) {
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
QueryResults<Reply> registerDate = queryFactory.select(reply).from(reply)
.where(reply.post.id.eq(id))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(OrderSpecifierFactory.getSortColumn(Order.DESC, reply, "registerDate"))
.fetchResults();
return new PageImpl(registerDate.getResults(),pageable,registerDate.getTotal());
}
테스트 코드
@Test
public void crud() {
PageRequest request = PageRequest.of(0,10);
Page<Reply> byPostIdWithPagination = replyRepository.findByPostIdWithPagination(1L, request);
System.out.println(byPostIdWithPagination.getContent().get(0).getPost().getSubject());
System.out.println("Check=======================");
byPostIdWithPagination.getContent().forEach(reply ->
System.out.println(reply.toString()));
}
결과
추가 쿼리문과 결과
댓글 페이지 네이션 정보에 글정보까지 함께 가져오는 게 너무...너무 아닌것 같아서 실험 시작
- 일단, post는 가져오지 못하게 막아주고 제네릭 클래스로 서비스에서 가져올 계획으로 응용해보려 시작
- Tuple 을 간과한 대가를 치름.
@Override
public Page<Reply> findByPostIdWithPagination(Long id, Pageable pageable) {
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
QueryResults<Tuple> registerDate = queryFactory.select(reply.id, reply.subject,
reply.author, reply.registerDate, reply.updateDate, reply.content)
.from(reply)
.where(reply.post.id.eq(id))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(OrderSpecifierFactory.getSortColumn(Order.DESC, reply, "registerDate"))
.fetchResults();
return new PageImpl(registerDate.getResults(),pageable,registerDate.getTotal());
}
Stack OverFlow 와 Github 질문들을 해석하며 문제를 파악한 결과 Tuple 은 직렬화 문제때문에 Jackson이 바인딩을 못한다고 함.
- 그리하여 문제를 해결하려고 한 결과 QueryDsl 에서 지원하는 Projections 를 채용
public interface PostCustomRepository<T> {
Post findOnePostByIdAndNotFetchReplies(Long id);
}
// 구현
@Repository
@Transactional
public class PostCustomRepositoryImpl implements PostCustomRepository<Post> {
@Autowired
EntityManager entityManager;
@Override
public Post findOnePostByIdAndNotFetchReplies(Long id) {
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
Post findPost = queryFactory.select(Projections.fields(Post.class,
post.id,post.subject,post.author,post.registerDate,post.updateDate,post.content,post.accountId))
.from(post)
.where(post.id.eq(id))
.fetchOne();
return findPost;
}
}
Post 서비스 테스트 결과 확인 후 원하는 쿼리 도출 확인 한 뒤 댓글 쿼리작업 진행
- 알아보기 힘들어서 메서드 명이 길더라도 확실하게 알아볼 수 있게 기입하기 시작.
- Projections 의 자태에 놀라기 시작.
피로도가 확 몰려오기 시작.
public interface ReplyCustomRepository<T> {
Page<Reply> findByPostIdWithPagination(Long id, Pageable pageable);
}
//구현
@Repository
@Transactional
public class ReplyCustomRepositoryImpl implements ReplyCustomRepository<Reply>{
@Autowired
private EntityManager entityManager;
@Override
public Page<Reply> findByPostIdWithPagination(Long id, Pageable pageable) {
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
QueryResults<Reply> registerDate = queryFactory.select(Projections.fields(
Reply.class, reply.id, reply.subject,
reply.author, reply.registerDate, reply.updateDate, reply.content
))
.from(reply)
.where(reply.post.id.eq(id))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(OrderSpecifierFactory.getSortColumn(Order.DESC, reply, "registerDate"))
.fetchResults();
return new PageImpl(registerDate.getResults(),pageable,registerDate.getTotal());
}
}
바라고 바라던 제네릭 클래스 생성
- 제네릭 지식을 바등바등 기억하며 어설프게 작성
- 이후 서비스, 컨트롤러 코드 간소하게 작성
@AllArgsConstructor
public class ArticleInfo<P,R> implements Serializable {
public P postInfo;
public R replyInfo;
}
//서비스
public interface ArticleService {
ArticleInfo<Post,Page<Reply>> getPostById(Long id);
}
//구현
@Service
@Slf4j
public class ArticleServiceImpl implements ArticleService{
@Autowired
private PostRepository postRepository;
@Autowired
ReplyRepository replyRepository;
@Override
@Transactional
public ArticleInfo<Post, Page<Reply>> getPostById(Long id) {
log.warn("getPostById, Parameter : {}",id);
Post notFetchReplies = postRepository.findOnePostByIdAndNotFetchReplies(id);
PageRequest request = PageRequest.of(0,10);
Page<Reply> replyPage = replyRepository.findByPostIdWithPagination(id, request);
ArticleInfo<Post, Page<Reply>> articleInfo = new ArticleInfo<>(notFetchReplies,replyPage);
return articleInfo;
}
}
///컨트롤러
@RestController
@Slf4j
public class TestController {
@Autowired
private ArticleService articleService;
@GetMapping("/test/{id}")
public ResponseEntity<Object> test(@PathVariable("id") Long id) {
try {
ArticleInfo<Post, Page<Reply>> articleInfo = articleService.getPostById(id);
return ResponseEntity.ok(articleInfo);
}catch (Exception ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());
}
}
}
일단 JSON 데이터는 잘보내지는데, 쿼리 생성 정보 기록
- id 를 기준으로 Post 정보 추출
- reply 의 총 레코드 수 추출
- RowNum 으로 페이지 네이션 잡아서 추출
'작업하면서 배우는 것들' 카테고리의 다른 글
MapStruct (0) | 2021.01.12 |
---|---|
SpringFox:Swagger3 사용 (0) | 2021.01.05 |
QueryDsl 과 커스텀 리포지토리를 이용한 쿼리문 사용 (0) | 2020.12.27 |
JPA Projection FindAll (0) | 2020.12.24 |
Vue Router 와 Href 속성 (0) | 2020.12.24 |