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()
    );
}