Spring JPA 使用@Query return 两个 DB2 TIMESTAMP 值之间的数据

Spring JPA Using @Query to return data between two DB2 TIMESTAMP values

我正在写一个 Springboot JPA REST API,它与 DB2 数据库对话,应该查询一个包含 TIMESTAMP 字段的 table。

使用 SQL,过滤两个 TIMESTAMP 之间的行的 DB2 查询如下所示,它将 return 来自我的测试数据的 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);
}

然而,这不起作用,它 return 没有数据,虽然我知道它应该 return 我 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() 方法的那些。

UPDATE-1

感谢@HYUNJUN,我添加了一些更改:

  1. 我像以前一样在我的实体中使用 java.sql.Timestamp 但我的 控制器、服务和存储库使用 java.util.Date 而不是 java.sql.Date 我最初使用的。
  2. 在我的 application.properties 下面添加,以便能够查看在 SQL 中传递了哪些参数(请注意,这会显着降低速度,因此仅用于调试目的): logging.level.org.hibernate.sql=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

现在,当我转到 DB2 Bench 并发出以下查询时,我将返回正确的 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);
}

, return 没什么,我希望 return 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]

因此,我将通过相同的日期范围并期望得到相同的结果,但这并没有发生

我写的代码和你上面的代码几乎一样。但是我没有遇到你提到的问题。我在下面上传我的代码。你为什么不比较我的代码和你的代码?

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

此外,将此选项放入 applicaion.yml 以检查是否将正确的值传递给 SQL。

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

像这样。

您可以使用以下代码检索所需的结果

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

我最终使用 java.sql.Timestamp 将它与我的 CarEntity 一起使用,如最初发布的那样,但这不会正确过滤数据,我会不断从我的控制器获取不同的记录与来自 SQL [=30 的记录=].

我发现像我在上面使用 java.sql.Datejava.sql.Timestampjava.util.Date 那样混合日期类型很可能是造成这种情况的原因,我怀疑日期转换已关闭导致我的控制器 return 我的数据错误。

然后我读了这篇文章 https://thorben-janssen.com/hibernate-jpa-date-and-time/ 并意识到即使我的 DB2 table 使用 TIMESTAMP,我也可以使用 java.sql.Date

因此,我修改了我的控制器、服务、存储库和实体以使用 java.util.Date 而不是使用 java.sql.Datejava.sql.Timestamp

这解决了我的问题。