无法将 java.util.Date 值与 MySQL 日期时间列值进行比较

Gur*_*rma 5 java hibernate spring-boot

我正在尝试从表中获取特定日期的结果。我使用 URL 以字符串格式传递日期,然后将字符串日期解析为 java.util.Date,然后使用它从表中获取值。在表中,值存储在数据类型为日期时间的列中。

@Query(value = "" +
        "FROM " +
        "   MarketingForm mf " +
        "WHERE " +
        "   (:createdAt is null OR mf.createdAt = :createdAt)" +
        "   AND (:verticalId is null OR mf.verticalId = :verticalId)" +
        "   AND (:tag is null OR mf.tag = :tag)"
)
List<MarketingForm> findAllByCreatedAtAndVerticalIdAndTag(@Param("createdAt") Date createdAt, @Param("verticalId") Long verticalId, @Param("tag") String tag);
Run Code Online (Sandbox Code Playgroud)

下面是字符串日期和相应的 java.util.Date

  • 字符串日期 = '2021-03-03'
  • java.util.Date 转换日期 = 'Wed Mar 03 00:00:00 IST 2021'

我用来将字符串日期解析为 java.util.Date 的方法 -

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
return formatter.parse(formCreatedDate);
Run Code Online (Sandbox Code Playgroud)

现在我不确定这里发生了什么。java.util.Date 没有被正确解析,或者我是否在做不正确的比较,但即使记录在表中,查询也无法获取结果。

为什么即使表中存在记录,上述 SQL 也无法获取指定日期的记录?

在此处输入图片说明

注意:表中的数据是时间格式,我只需要比较日期的日期分量即可。

Bas*_*que 6

由于您的问题不清楚,我不确定您的确切期望,因此我无法给出准确的答案。但是我可以讨论您的方法和代码中的一些问题。

首先,永远不要使用java.util.Date. 这个可怕的类是旧日期时间类的一部分,这些类在多年前被JSR 310 中定义的现代java.time类所取代。同样,永远不要使用,也永远不要使用。SunOracleJCP社区在采用 JSR 310 后放弃了这些遗留类,您也应该如此。JPAHibernate都已更新以支持java.time类,就像JDBC 4.2规范一样。CalendarSimpleDateFormat

另一个可能的问题,也许您正在java.sql.Datejava.util.Date. 小心你import在那里的陈述。而且,再一次,永远不要使用任何一个Date类。

你提到了一串2021-03-03,但并没有真正解释它在哪里。无论如何,这代表一个仅限日期的值。但是你说你访问了DATETIMEMySQL 中的一列。根据MySQL 8 文档,该类型表示带有时间的日期。

DATETIME 类型用于同时包含日期和时间部分的值。MySQL 以 'YYYY-MM-DD hh:mm:ss' 格式检索和显示 DATETIME 值。支持的范围是“1000-01-01 00:00:00”到“9999-12-31 23:59:59”。

因此,通过仅日期值查询日期时间列是没有意义的。数据库无法将仅日期值与带时间日期的值进行比较。您需要为您的开始和结束指定一个时间跨度,一对带有时间的日期值。

另一个问题:您正在尝试使用java.util.Datewhich 代表一个时刻,在 UTC 上下文中看到的带有时间的特定日期 - 但DATETIMEMySQL 中的类型是一种不同的动物。DATETIMEMySQL 中的类型表示具有时间的日期,但缺少偏移量或时区的上下文。这种类型类似于 SQL 类型TIMESTAMP WITHOUT TIME ZONE

当心:如果您试图表示数据库中的时刻、时间线上的特定点,那么的列使用了错误的数据类型。暂时,您必须使用类似于 SQL 标准的数据类型TIMESTAMP WITH TIME ZONE。在 MySQL 8 中,这就是TIMESTAMP类型。TIMESTAMPMySQL 中的类型跟踪在 UTC 中看到的时刻,总是在 UTC 中。如果您有问题的列是名为 的列createdAt,那么您几乎肯定使用了错误的数据类型,因为必须使用 SQL-standard 来跟踪作为历史记录传递的时刻TIMESTAMP WITH TIME ZONE。但是我将忽略这个关键问题,并继续处理您在问题中指定的书面类型。

如果您跟踪时刻,例如跟踪保存在附加列中的时区旁边的未来约会,那么您的使用DATETIME将是正确的。如果您确定此数据类型适合您的目的,那么在 Java 中适当的匹配将是LocalDateTime类。

DATETIME通过使用半开放方法来定义时间跨度,对列上一天的数据进行查询。开始是包容性的,而结束是排斥性的。因此,一天从一天的第一时刻开始,一直到(但不包括)第二天的开始。对于半开,我们使用 SQL 关键字BETWEEN

这是一个简单的代码示例以适应您的情况。我既不使用 Spring 也不使用 Hibernate,因此您必须相应地进行翻译。

对于仅限日期的输入字符串,解析为LocalDate.

提示:询问“大于或等于”的简短方法是“不小于”。我们在下面的 SELECT 语句中的谓词的第一种情况下看到了这一点。

LocalDate ld = LocalDate.parse( "2021-03-03" ) ;
LocalDateTime startOfDay = ld.atStartOfDay() ;
LocalDateTime startOfNextDay = ld.plusDays( 1 ).atStartOfDay() ;
String sql = "SELECT * FROM my_table WHERE when !< ? AND when < ? ;" ;  // Performing query over a span-of-time defined as Half-Open. 
…
myPreparedStatement.setObject( 1 , startOfDay ) ;
myPreparedStatement.setObject( 2 , startOfNextDay ) ;
Run Code Online (Sandbox Code Playgroud)

检索DATETIME值。

LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
Run Code Online (Sandbox Code Playgroud)

所有这些已经在 Stack Overflow 上讨论过很多次了。搜索以了解更多信息。

Java(旧版和现代版)和标准 SQL 中的日期时间类型表