1
resposta

Como consultar um campo LocalDate do JPA passando um mês e um ano como argumentos?

Eu tenho a seguinte entidade com um campo LocalDate transactionDate:

public class Revenue {

... (Outros campos omitidos para simplicidade)

  @Column(name = "transaction_date")
  private LocalDate transactionDate;

Eu quero pegar todas as receitas de um mês específico. A URI deve ser algo como: /revenue/{ano}/{mês}.

Eh possivel fazer isso com métodos de consulta do JPA out-of-the-box?

Já tentei fazer o seguinte:

@Repository
public interface RevenueRepository extends JpaRepository<Revenue, Long> {

  @Query(
      "SELECT r FROM revenue WHERE YEAR(r.transactionDate) = :year AND MONTH(r.transactionDate) = :month")
  Page<Revenue> findByYearAndMonth(
      @Param("year") int year, @Param("month") int month, Pageable pageable);


...

public interface RevenueService {

  Page<RevenueDTOResponse> getByYearAndMonth(int year, int month, Pageable pageable);

...

@Service
public class RevenueServiceImpl implements RevenueService {

  @Autowired private RevenueRepository revenueRepository;

  @Override
  public Page<RevenueDTOResponse> getByYearAndMonth(int year, int month, Pageable pageable) {
    return revenueRepository.findByYearAndMonth(year, month, pageable).map(RevenueDTOResponse::new);
  }

...

  @GetMapping("/{year}/{month}")
  public ResponseEntity<Page<RevenueDTOResponse>> getByYearAndMonth(
      @PathVariable int year, int month, Pageable pageable) {
    return new ResponseEntity<>(
        revenueService.getByYearAndMonth(year, month, pageable), HttpStatus.OK);
  }

Mas a seguinte exceção estava sendo lançada: https://gist.github.com/davisaints/764515ff10d2113afd41844df3695a37

1 resposta

Opa, David! Tudo bem?

O erro que você está vendo indica que há um problema com a criação da consulta JPQL. Vou ajudar a corrigir isso.

A sua consulta JPQL possui alguns problemas:

  1. A sintaxe da JPQL está incorreta. O nome da entidade precisa ser usado corretamente na consulta.

  2. O uso de funções YEAR e MONTH não é suportado diretamente no JPQL. Em vez disso, você pode usar a função DATE_TRUNC (se estiver disponível no seu banco de dados) ou criar uma consulta baseada em intervalos de datas.

Para filtrar por ano e mês, você pode construir uma consulta que usa um intervalo de datas. Por exemplo, para o mês de julho de 2024, você pode construir uma consulta que busca receitas onde a data de transação está entre 1º de julho e 31 de julho.

Aqui está como você pode modificar seu RevenueRepository:

@Repository
public interface RevenueRepository extends JpaRepository<Revenue, Long> {

    @Query("SELECT r FROM Revenue r WHERE r.transactionDate BETWEEN :startDate AND :endDate")
    Page<Revenue> findByMonth(
        @Param("startDate") LocalDate startDate, 
        @Param("endDate") LocalDate endDate, 
        Pageable pageable);
}

Em seguida, ajuste seu serviço para construir essas datas:

@Service
public class RevenueServiceImpl implements RevenueService {

    @Autowired
    private RevenueRepository revenueRepository;

    @Override
    public Page<RevenueDTOResponse> getByYearAndMonth(int year, int month, Pageable pageable) {
        LocalDate startDate = LocalDate.of(year, month, 1);
        LocalDate endDate = startDate.withDayOfMonth(startDate.lengthOfMonth()); // Último dia do mês
        return revenueRepository.findByMonth(startDate, endDate, pageable).map(RevenueDTOResponse::new);
    }
}

E o seu controlador ficaria assim:

@RestController
@RequestMapping("/revenue")
public class RevenueController {

    @Autowired
    private RevenueService revenueService;

    @GetMapping("/{year}/{month}")
    public ResponseEntity<Page<RevenueDTOResponse>> getByYearAndMonth(
        @PathVariable int year, 
        @PathVariable int month, 
        Pageable pageable) {
        Page<RevenueDTOResponse> revenues = revenueService.getByYearAndMonth(year, month, pageable);
        return ResponseEntity.ok(revenues);
    }
}
  • Certifique-se de que a entidade Revenue está nomeada corretamente em seu JPQL. O nome da entidade deve ser Revenue (com "R" maiúsculo) e não revenue.

  • Verifique se o campo transactionDate está mapeado corretamente e se não há problemas de nomeação ou configuração.

Se você estiver usando um banco de dados que suporta funções específicas de data, como PostgreSQL, você pode ajustar a consulta para usar essas funções diretamente.

Espero que essas sugestões ajudem a resolver o seu problema. Caso não resolvam, peço que compartilhe comigo o link do repositório do seu projeto para que eu realize testes.

Bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.