【问题标题】:MySQL DB or JDBC driver not handling LocalDate.MAXMySQL DB 或 JDBC 驱动程序不处理 LocalDate.MAX
【发布时间】:2018-03-23 16:20:51
【问题描述】:

我正在尝试使用开始日期和结束日期作为参数运行查询,这可能为空。

MySQL - DB 或 JDBC 驱动程序或组合 - 无法处理空值,我的意思是,SQL AND endDate <= null 排除每一行。

所以我尝试使用startDate >= LocalDate.MIN,它适用于“无开始日期”。

但是endDate <= LocalDate.MAX 不允许任何日期。这是 SQL 模板:

String strStmt = "SELECT d.forecastId, d.valueDate, d.value " +
        "FROM data d " +
        "WHERE d.forecastId = ? " +
        "AND valueDate >= ? " +
        "AND valueDate <= ? " +

这是变量分配:

if (startDate == null) {
    statement.setObject(2, LocalDate.MIN);
} else {
    statement.setObject(2, startDate.toInstant().atZone(
            ZoneId.systemDefault()).toLocalDate());
}
if (endDate == null) {
    statement.setObject(3, LocalDate.MAX);
} else {
    statement.setObject(3, endDate.toInstant().atZone(
            ZoneId.systemDefault()).toLocalDate());
}
try (ResultSet rs = statement.executeQuery()) { 
...

这是它生成的 SQL(通过 P6Spy):

SELECT valueDate FROM data d WHERE d.forecastId = 52010 
AND valueDate >= '-999999999-01-01' 
AND valueDate <= '+999999999-12-31'

这不起作用,我没有行。

如果我再把它改成不那么极端的东西:

if (endDate == null) {
    statement.setObject(3, LocalDate.parse("2019-01-01"));
...

我在 P6Spy 日志中看到以下内容:

SELECT valueDate FROM data d WHERE d.forecastId = 52010 
AND valueDate >= '-999999999-01-01' 
AND valueDate <= '2019-01-01'

并且查询工作正常,当然在将来的某个时候除外。

这是 MySQL 中的某种溢出吗?我应该以不同的方式使用LocalDate.MAX,还是可以使用什么来代替LocalDate.MAX

我不想回到

    statement.setDate(3, new java.sql.Date(Long.MAX_VALUE); 

产生以下内容:

AND valueDate <= '17-Aug-94'

哪个也不起作用 - 也许是 Y2K 或 2038 年的错误? P6Spy 以自己无用的格式显示它并没有帮助。

【问题讨论】:

  • 您能否更改查询本身,即如果日期不为空,则仅动态包含其他日期比较?
  • 阅读文档? 支持的范围是 '1000-01-01' 到 '9999-12-31'。 mariadb.com/kb/en/library/date
  • 与您的问题无关,但Connector/J 6.0.4是Connector/J废弃版本6的开发版本,不是生产就绪版本。最后发布的版本是 5.1.44。最新的开发版本是 8.0.8(但该版本还没有生产就绪!)。
  • @MarkRotteveel 哦,对了。我刚刚抓住了 6.0.6,因为 IntelliJ 在 pom.xml 中自动完成了它。我在 mysql bugzilla 中同时遇到了字符编码和相关 bug 的其他问题,所以我尝试对其进行更改,但 IntelliJ 似乎坚持使用它的 jar 来等待严峻的死亡,这使得更改版本变得乏味。我现在正在试用 mariadb connector/j 2.1.2

标签: java mysql date jdbc


【解决方案1】:

假设您不打算过滤掉 valueDate 中具有 null 值的行,那么您似乎正在尝试创建一个条件,该条件在开始日期或结束日期时始终评估为 true在查询中是null。我认为通过动态生成 SQL 来省略 null 结束/开始日期案例中的(冗余)比较会更简单:

String strStmt = "SELECT d.forecastId, d.valueDate, d.value " +
        "FROM data d " +
        "WHERE d.forecastId = ? ";

if (startDate != null) {
    strStmt += "AND valueDate >= ? ";
}
if (endDate != null) {
    strStmt += "AND valueDate <= ?";
}

然后,在创建语句后,您将动态添加正确数量的日期参数:

int index = 1;

statement.setInt(index++, forecastId);

if (startDate != null) {
    statement.setObject(index++, startDate.toInstant().atZone(
            ZoneId.systemDefault()).toLocalDate());
}
if (endDate != null) {
    statement.setObject(index, endDate.toInstant().atZone(
            ZoneId.systemDefault()).toLocalDate());
}

try (ResultSet rs = statement.executeQuery()) {
 //...

【讨论】:

  • 不,我无法更改查询。我必须对当前应用程序的 Java 中的嵌入式 SQL 进行快速语法更改,才能从 SQL Server 更改为 MySQL,这部分被很多方法使用。如果我没有找到合理的常量值,我会考虑花时间重构整个模块。
【解决方案2】:

超过 MySQL 中数据类型 DATE 的限制

MySQL 5.7 中的数据类型DATE 被限制为比java.time.LocalDate 类型更小的值范围。引用该文档,(强调我的):

DATE 类型用于具有日期部分但没有时间部分的值。 MySQL 以 'YYYY-MM-DD' 格式检索和显示 DATE 值。 支持的范围是“1000-01-01”到“9999-12-31”

相比之下,java.time.LocalDate.MAX 是日期 +999999999-12-31。 999999999 年远远超过 9999 年。

如果您的目标是记录遥远未来的某个特定日期作为未知或不确定日期的替代,请选择一个不太远的日期。或者使用NULL,但Dr. Chris Date我也不推荐使用NULL。

日期时间功能在各种数据库中差异很大。 SQL 标准几乎没有涉及这个主题。因此,您必须仔细阅读特定数据库的文档,以了解该产品的限制和行为。同样,您的 JDBC 驱动程序。

【讨论】:

  • 好帖子 :) 我正在开发的应用程序是一个预测应用程序,它为 Excel 插件、Python、r 和其他客户端提供服务。我们运行的负载测试之一(诚然不是真正使用)涉及将 250,000 个每日日期添加到未来 (2684)。我想 9999 离未来太远了,但不费吹灰之力,我们已经完成了四分之一。我将看看是否可以在 JDBC 库中找到一个 MySQL 最大日期常量,我可以使用它来代替 LocalDate.MAX。如果应用程序被重写,我认为放弃 DATE 类型是有意义的。我看到没有 BIGDATE。
猜你喜欢
  • 1970-01-01
  • 2013-02-08
  • 2011-09-10
  • 2016-03-27
  • 2016-04-02
  • 2015-09-11
  • 2014-09-11
  • 2014-10-24
  • 1970-01-01
相关资源
最近更新 更多