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 | 29 | 30 | 31 |
Tags
- 이분탐색
- ES
- Algorithm
- 완전탐색
- 이분 탐색
- programmers
- OOP
- SpringBoot
- 백준
- 브루트포스
- 코딩테스트
- parametric search
- 리팩토링
- querydsl
- Generics
- til
- 프로그래머스
- Elasticsearch
- BFS
- Spring
- 일정 관리
- binary search
- Java
- 누적합
- 내일배움캠프
- 구현
- 알고리즘
- 객체지향
- 계산기 만들기
- Baekjoon
Archives
- Today
- Total
개발하는 햄팡이
[Spring][일정 관리 앱 만들기] CRUD 생성 - QueryDsl 쿼리문 코드 작성 본문
이전에 이어서 이제 내가 원하는 API를 구현할 차례이다.
전체 일정 조회를 하는 기능을 추가하려고 하는데 수정일과 작성자Id를 선택으로 받아 null인지 아닌지 체크하여 동적쿼리를 작성해야 한다.
나는 일정 페이지에서 -내가 작성한 OO일 일정들-을 보고싶어서 아래 API를 작성한 것이기 때문에 일치 여부를 판단하는 쿼리를 작성할 것이다.
저번에 구현체 클래스에 로직을 작성하면 된다고 했다.
그래서 구현체 클래스에 아래와 같은 쿼리문을 작성했다.
package com.sparta.schedule.schedule.repository;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.sparta.schedule.schedule.entity.QSchedule;
import com.sparta.schedule.schedule.entity.Schedule;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
import java.util.List;
@Repository
@RequiredArgsConstructor
public class ScheduleRepositoryCustomImpl implements ScheduleRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public List<Schedule> findSchedulesByUpdatedAtAndWriterId(LocalDate updatedAt, Long writerId) {
QSchedule schedule = QSchedule.schedule;
BooleanBuilder builder = new BooleanBuilder();
if (updatedAt != null) {
builder.and(schedule.updatedAt.eq(updatedAt));
}
if (writerId != null) {
builder.and(schedule.user.id.eq(writerId));
}
return queryFactory.selectFrom(schedule)
.where(builder)
.fetch();
}
}
일단 이렇게 하고 테스트하러 고고
파라미터를 넣든 말든 원하는 값이 잘 나온다.
전체 코드
Controller
@RestController
@RequestMapping("/schedules")
@RequiredArgsConstructor
public class ScheduleController {
private final ScheduleService scheduleService;
@PostMapping
public ResponseEntity<ScheduleDto> createSchedule(@RequestBody CreateScheduleRequestDto request) {
ScheduleDto response = scheduleService.createSchedule(
request.getTitle(),
request.getContents(),
request.getWriterId()
);
return new ResponseEntity<>(response, HttpStatus.CREATED);
}
@GetMapping("/{id}")
public ResponseEntity<ScheduleDto> getSchedule(@PathVariable Long id) {
ScheduleDto response = scheduleService.getSchedule(id);
return new ResponseEntity<>(response, HttpStatus.OK);
}
@GetMapping
public ResponseEntity<List<ScheduleDto>> getSchedules(@RequestParam(value = "updatedAt", required = false)LocalDate updatedAt,
@RequestParam(value = "writerId", required = false) Long writerId) {
List<ScheduleDto> response = scheduleService.getSchedulesByUpdatedAtAndWriterId(updatedAt, writerId);
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
Service
@Service
@RequiredArgsConstructor
public class ScheduleService {
private final UserRepository userRepository;
private final ScheduleRepository scheduleRepository;
public ScheduleDto createSchedule(String title, String contents, Long writerId) {
Schedule newSchedule = new Schedule(title, contents);
newSchedule.setUser(userRepository.findByIdOrElseThrow(writerId));
Schedule saved = scheduleRepository.save(newSchedule);
return ScheduleMapper.toDto(saved);
}
public ScheduleDto getSchedule(Long id) {
Schedule findSchedule = scheduleRepository.findByIdOrElseThrow(id);
return ScheduleMapper.toDto(findSchedule);
}
public List<ScheduleDto> getSchedulesByUpdatedAtAndWriterId(LocalDate updatedAt, Long writerId) {
List<Schedule> result = scheduleRepository.findSchedulesByUpdatedAtAndWriterId(updatedAt, writerId);
return result.stream()
.map(ScheduleMapper::toDto)
.collect(Collectors.toList());
}
}
Repository
public interface ScheduleRepository extends JpaRepository<Schedule, Long>, ScheduleRepositoryCustom {
default Schedule findByIdOrElseThrow(long id) {
return findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "게시글을 찾을 수 없습니다."));
}
}
public interface ScheduleRepositoryCustom {
List<Schedule> findSchedulesByUpdatedAtAndWriterId(LocalDate updatedAt, Long writerId);
}
@Repository
@RequiredArgsConstructor
public class ScheduleRepositoryCustomImpl implements ScheduleRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public List<Schedule> findSchedulesByUpdatedAtAndWriterId(LocalDate updatedAt, Long writerId) {
QSchedule schedule = QSchedule.schedule;
BooleanBuilder builder = new BooleanBuilder();
if (updatedAt != null) {
builder.and(schedule.updatedAt.eq(updatedAt));
}
if (writerId != null) {
builder.and(schedule.user.id.eq(writerId));
}
return queryFactory.selectFrom(schedule)
.where(builder)
.fetch();
}
}
++
아직 페이징을 하고 있지 않지만 예전에 페이지네이션을 적용하는 쿼리를 작성했을땐
AbstractQueryDslRepository 를 만들어 getPageImpl(query, pageable)메소드를 만들고 중복 코드를 줄였었다.
@RequiredArgsConstructor
public class AbstractQueryDslRepository {
protected final JPAQueryFactory queryFactory;
private final EntityManager entityManager;
private Map<String, Querydsl> querydslMap = new HashMap<>();
protected <T> Querydsl getQuerydsl(Class<T> clazz) {
return querydslMap.computeIfAbsent(clazz.getName(), key -> {
PathBuilder<T> builder = new PathBuilderFactory().create(clazz);
return new Querydsl(entityManager, builder);
});
}
protected <T> Page<T> getPageImpl(JPQLQuery<T> query, Pageable pageable) {
long totalCount = query.fetchCount();
List<T> results = getQuerydsl(query.getType()).applyPagination(pageable, query).fetch();
return new PageImpl<>(results, pageable, totalCount);
}
}
이런식으로 구현해서 구현체에 상속하면 계속 JPAQueryFactory나 EntityManager를 가져올 필요가 없어지고 Page처리를 하는 로직이 확 줄어든다.
아래는 예전에 작성했던 Repository이다.
public class UserRepositoryCustomImpl extends AbstractQueryDslRepository implements UserRepositoryCustom {
public UserRepositoryCustomImpl(JPAQueryFactory queryFactory, EntityManager entityManager) {
super(queryFactory, entityManager);
}
@Override
public Page<User> findUsersByRoleId(String roleId, Pageable pageable) {
QUser user = QUser.user;
QUserRole userRole = QUserRole.userRole;
JPQLQuery<User> query = from(user).join(user.userRoles, userRole)
.where(userRole.roleId.eq(roleId))
.select(user);
return getPageImpl(query, pageable);
}
@Override
public Page<User> getUsersByChannelId(String channelId, Pageable pageable) {
// 입력받은 channelId 가 현재 유저가 참여한 채널 중 존재하는 channelId일 경우
QUser user = QUser.user;
QUserChannel userChannel = QUserChannel.userChannel;
JPQLQuery<User> query = from(user)
.join(user.userChannels, userChannel)
.where(userChannel.channelId.eq(channelId));
return getPageImpl(query, pageable);
}
}
이렇게 하면 더 깔끔하게 할 수 있다.
나중에 페이지네이션이 요구사항으로 들어오면 위와 같이 구현할 예정이다.
'Back-End > Spring' 카테고리의 다른 글
[Spring][일정 관리 앱 만들기] CRUD 생성 - QueryDsl 시작하기 (2) | 2025.05.24 |
---|---|
[ES, SpringBoot] 공지사항 게시판 검색 기능 구현하기 (2) : 데이터 저장 및 삭제 (0) | 2024.11.12 |
[Springboot, Mysql, Mybatis] PasswordEncoder구현 - 검색 효율 높인 게시판 구현 (0) | 2024.10.23 |