Spring JPA использует @Query для возврата данных между двумя значениями временных меток DB2

#spring-boot #spring-data-jpa #timestamp #db2

Вопрос:

Я пишу Springboot JPA REST API, который взаимодействует с базой данных DB2 и должен запрашивать таблицу, содержащую TIMESTAMP поле.

Используя SQL, запрос DB2 для фильтрации строк между двумя временными метками будет выглядеть следующим образом, и он вернет 1 запись из моих тестовых данных:

 SELECT * FROM CARS WHERE SOLD_DATE BETWEEN '2020-01-01' AND '2022-01-01'
 

Поскольку я использую Spring Data JPA, я определил CarEntity , у которого есть java.sql.Timestamp поле

 @Entity
public class CarEntity {
   ....
   Timestamp soldDate;
   ...
   //getters and setters
}
 

Я пытаюсь получить данные, как в приведенном выше SQL-запросе.

Для этого я передаю начальные и конечные данные в Postman в виде Long значений, представляющих начальную и конечную дату, через URL, например

 http://localhost:8080/cars/sold/1420070400/1640995200
 

Эта конечная точка попадает в мой метод контроллера, который преобразует Long в java.sql.Date и передает его в репозиторий, а в репозитории я использую @Query аннотацию, как показано ниже:

 @Repository
public interface CarRepository extends JpaRepository<CarEntity, Timestamp>{
    @Query("select c from CarEntity c where c.carModel = 'Toyota' and c.soldDate between :startDate and :endDate") 
    List<CarEntity> getCarsSoldBetween(Date startDate, Date endDate);
}
 

Однако это не работает и не возвращает никаких данных, хотя я знаю, что он должен вернуть мне 1 запись.

Но если я жестко закодирую дату начала и окончания, как показано ниже, я получу 1 запись:

 @Query("select c from CarEntity c where c.carModel = 'Toyota' and c.soldDate between '2020-01-01' and '2022-01-01'") 
List<CarEntity> getCarsSoldBetween(Date startDate, Date endDate);
 

Конечно, проблема в том, что я жестко запрограммировал StartDate и EndDate вместо использования переданных в getCarsSoldBetween() метод.

ОБНОВЛЕНИЕ-1

Благодаря @HYUNJUN я добавил пару изменений:

  1. Я использую java.sql.Timestamp в своей сущности, как и раньше, но мой контроллер, сервис и репозиторий используют java.util.Date вместо java.sql.Date того, что я использовал изначально.
  2. В моем приложении.свойства добавлены ниже, чтобы иметь возможность просматривать, какие параметры передаются в SQL (обратите внимание, что это приводит к значительному замедлению, поэтому используйте только для целей отладки): logging.level.org.hibernate.sql=DEBUG
    logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

Теперь, когда я захожу на стенд DB2 и выполняю следующий запрос, я получу 2 строки обратно, что правильно:

 SELECT * FROM MYSCHEMA.CARS WHERE SOLD_TIMESTAMP BETWEEN '2021-10-04 15:00:00' AND '2021-10-20 00:00:00';

// RETURNS 2 ROWS
 

Однако мой запрос к репозиторию, который выглядит как:

 @Repository
public interface CarRepository extends JpaRepository<CarEntity, Timestamp>{
    @Query("select c from CarEntity c where c.carModel = 'Toyota' and c.soldDate between :startDate and :endDate") 
    List<CarEntity> getCarsSoldBetween(Date startDate, Date endDate);
}
 

, ничего не возвращает, и я ожидал бы вернуть 2 строки, поскольку дата начала и окончания совпадают с выводом журнала:

 type.descriptor.sql.BasicBinder  binding parameter [1] as [TIMESTAMP] - [Mon Oct 04 15:00:00 PDT 2021]
type.descriptor.sql.BasicBinder  binding parameter [2] as [TIMESTAMP] - [Wed Oct 20 00:00:00 PDT 2021]
 

Итак, я передаю тот же диапазон дат и ожидаю того же результата, но этого не происходит

Комментарии:

1. Попробуйте просмотреть журналы, чтобы увидеть, какой запрос фактически выполняется в базе данных. Я подозреваю, что это отличается от того, что вы хотите. Добавьте spring.jpa.show-sql=true в файл .properties .

Ответ №1:

Я написал код, почти совпадающий с вашим кодом выше. Но я не столкнулся с проблемой, о которой вы упомянули. Я загружаю свой код ниже. Почему вы не сравниваете мой код со своим кодом?

 package com.springboot.springbootinternals.db2;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class Db2Controller {

    private final CarRepository carRepository;

    @GetMapping("db2/{start-date}/{end-date}")
    public String db2(
        @PathVariable("start-date") Long startDate,
        @PathVariable("end-date") Long endDate
    ) {

        carRepository.save(Car.builder().date(new Timestamp(1420070401L * 1000)).build());

        Date start = new Date(TimeUnit.SECONDS.toMillis(startDate));
        Date end = new Date(TimeUnit.SECONDS.toMillis(endDate));
        List<Car> cars = carRepository.findAll(start, end);
        System.out.println(">>> "   cars);
        return "success";
    }
}

@Entity
@Builder
@NoArgsConstructor
@ToString
@AllArgsConstructor
class Car {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Timestamp date;
}

@Repository
interface CarRepository extends JpaRepository<Car, Long> {

    @Query("select c from Car c where c.date between :startDate and :endDate")
    List<Car> findAll(Date startDate, Date endDate);
}
 

Кроме того, поместите эту опцию в приложение.yml для проверки правильного значения передается в SQL.

 logging:
  level:
    org:
      hibernate:
        SQL: DEBUG
        type:
          descriptor:
            sql:
              BasicBinder: TRACE # show_parameter_value
 

вот так.

введите описание изображения здесь

Комментарии:

1. Спасибо, но все еще проблема. Теперь мой запрос не возвращает ожидаемый результат. Пожалуйста, смотрите UPDAE-1 , который я только что добавил.

Ответ №2:

В конце концов я заставил его работать с моим CarEntity, используя java.sql.Timestamp as originally posted, но это не будет корректно фильтровать данные, и я буду продолжать получать разные записи от моего контроллера по сравнению с записями из SQL SELECT.

Я выяснил, что причиной этого, скорее всего, было смешивание типов дат, как я делал выше, где я использовал java.sql.Date или java.sql.Timestamp с java.util.Date , и я подозреваю, что преобразование даты было отключено, из-за чего мой контроллер возвращал мне неправильные данные.

Затем я прочитал эту статью https://thorben-janssen.com/hibernate-jpa-date-and-time / и понял, что даже несмотря на то, что моя таблица DB2 использует ВРЕМЕННУЮ МЕТКУ, я могу использовать java.sql.Date .

Итак, я изменил свой контроллер, службу, репозиторий и объект для использования java.util.Date вместо использования java.sql.Date или java.sql.Timestamp .

Это устранило мою проблему.

Ответ №3:

Для получения желаемых результатов можно использовать следующий код

 @Repository
public interface CarEntityRepository extends JpaRepository<CarEntity,Long> {

    @Query("SELECT c FROM CarEntity c WHERE c.carModel like %:model% and c.soldDate  > :start and  c.soldDate  < :end ")
    List<CarEntity> getCarsSoldBetween(@Param("model") String model, @Param("start") Date start, @Param("end") Date end);
}
 

Комментарии:

1. Спасибо, Мехди, но я не думаю, что вы поняли мой вопрос