T.I.L
spring-plus 주차 과제 회고
skawlsgus2
2024. 10. 11. 10:21
1. 특정 조건으로 검색하기(JPQL)
weather 조건과 수정일 조건(기간)을 기준으로 검색하는 기능을 만들었는데
수정일 조건으로 검색했을 때 작동이 되지 않는 문제가 있었다.
@GetMapping("/todos")
public ResponseEntity<Page<TodoResponse>> getTodos(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String weather,
@RequestParam(required = false) List<LocalDate> dates
) {
return ResponseEntity.ok(todoService.getTodos(page, size, weather, dates));
}
여기서 dates 는 LocalDate를 담는 리스트 형태인데 문제는 timestamp 를 상속받는 todo 엔티티가 가지고 있는
modifiedAt 이 LocalDateTime 형태라는 것. 서비스 클래스에서 형 변환해 주어 해결할 수 있었다.
@Transactional(readOnly = true)
public Page<TodoResponse> getTodos(int page, int size, String weather, List<LocalDate> dates) {
Pageable pageable = PageRequest.of(page - 1, size);
Page<Todo> todos;
if(weather == null && dates == null) {
// weather 조건, 조회 기간 없을 때
todos = todoRepository.findAllByOrderByModifiedAtDesc(pageable);
} else if(weather != null && dates == null) {
// weather 조건만 있을 때
todos = todoRepository.findByWeatherOrderByModifiedAtDesc(pageable, weather);
} else if(weather == null ) {
// 조회 기간만 있을 때
LocalDateTime startDate = dates.get(0).atStartOfDay();
LocalDateTime endDate = dates.get(1).atStartOfDay();
todos = todoRepository.findByPeriodOrderByModifiedAtDesc(pageable,startDate,endDate);
} else {
// 두 조건 다 있을 때
LocalDateTime startDate = dates.get(0).atStartOfDay();
LocalDateTime endDate = dates.get(1).atStartOfDay();
todos = todoRepository.findByWeatherAndPeriodOrderByModifiedAtDesc(pageable,weather,startDate,endDate);
}
return todos.map(todo -> new TodoResponse(
todo.getId(),
todo.getTitle(),
todo.getContents(),
todo.getWeather(),
new UserResponse(todo.getUser().getId(), todo.getUser().getEmail()),
todo.getCreatedAt(),
todo.getModifiedAt()));
}
2. 쿼리 DSL 적용하기
쿼리 DSL을 적용하는 과정을 정리해 보았다.
1) gradle 추가 -> build (Q클래스 만들어졌는지 확인)
// querydsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
2) Repository 만들기
package org.example.expert.domain.todo.repository;
import org.example.expert.domain.todo.entity.Todo;
import java.util.Optional;
public interface TodoDSLRepository {
Optional<Todo> findByTodoIdDSL(Long id);
}
3) 구현체 만들기
package org.example.expert.domain.todo.repository;
import com.querydsl.jpa.JPQLQueryFactory;
import lombok.RequiredArgsConstructor;
import org.example.expert.domain.todo.entity.Todo;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import static org.example.expert.domain.todo.entity.QTodo.todo;
@Repository
@RequiredArgsConstructor
public class TodoDSLRepositoryImpl implements TodoDSLRepository{
private final JPQLQueryFactory jpqlQueryFactory;
@Override
public Optional<Todo> findByTodoIdDSL(Long id) {
return Optional.ofNullable(jpqlQueryFactory.select(todo)
.from(todo)
.leftJoin(todo.user)
.where(todo.id.eq(id))
.fetchOne());
}
}
4) 원래 Repository에서 DSL Repository 상속 받기
package org.example.expert.domain.todo.repository;
import org.example.expert.domain.todo.entity.Todo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.time.LocalDateTime;
import java.util.Optional;
public interface TodoRepository extends JpaRepository<Todo, Long>, TodoDSLRepository {
@Query("SELECT t FROM Todo t LEFT JOIN FETCH t.user u ORDER BY t.modifiedAt DESC")
Page<Todo> findAllByOrderByModifiedAtDesc(Pageable pageable);
@Query("SELECT t FROM Todo t LEFT JOIN FETCH t.user u WHERE t.weather = :weather ORDER BY t.modifiedAt DESC")
Page<Todo> findByWeatherOrderByModifiedAtDesc(Pageable pageable, @Param("weather") String weather);
@Query("SELECT t FROM Todo t LEFT JOIN FETCH t.user u WHERE t.modifiedAt between :startDate AND :endDate ORDER BY t.modifiedAt DESC")
Page<Todo> findByPeriodOrderByModifiedAtDesc(Pageable pageable,
@Param("startDate") LocalDateTime startDate,
@Param("endDate") LocalDateTime endDate);
@Query("SELECT t FROM Todo t LEFT JOIN FETCH t.user u WHERE t.weather = :weather AND t.modifiedAt between :startDate AND :endDate ORDER BY t.modifiedAt DESC")
Page<Todo> findByWeatherAndPeriodOrderByModifiedAtDesc(Pageable pageable,
@Param("weather") String weather,
@Param("startDate") LocalDateTime startDate,
@Param("endDate") LocalDateTime endDate);
@Query("SELECT t FROM Todo t " +
"LEFT JOIN t.user " +
"WHERE t.id = :todoId")
Optional<Todo> findByIdWithUser(@Param("todoId") Long todoId);
}
4)적용하기
@Transactional(readOnly = true)
public TodoResponse getTodo(long todoId) {
Todo todo = todoRepository.findByTodoIdDSL(todoId)
.orElseThrow(() -> new InvalidRequestException("Todo not found"));
User user = todo.getUser();
return new TodoResponse(
todo.getId(),
todo.getTitle(),
todo.getContents(),
todo.getWeather(),
new UserResponse(user.getId(), user.getEmail()),
todo.getCreatedAt(),
todo.getModifiedAt()
);
}