#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 я добавил пару изменений:
- Я использую
java.sql.Timestamp
в своей сущности, как и раньше, но мой контроллер, сервис и репозиторий используютjava.util.Date
вместоjava.sql.Date
того, что я использовал изначально. - В моем приложении.свойства добавлены ниже, чтобы иметь возможность просматривать, какие параметры передаются в 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. Спасибо, Мехди, но я не думаю, что вы поняли мой вопрос