【问题标题】:More elegant way to insert empty java.sql.Date in Sql DB在 Sql DB 中插入空 java.sql.Date 的更优雅方式
【发布时间】:2012-09-17 11:02:22
【问题描述】:

您好,我正在尝试通过 Servlet 在 SQL 数据库中插入日期。如果帖子提交的字段生日是空的,那么它应该插入 0000-00-00

但这有点复杂。

我是这样做的,而且效果很好:

if (birthdate == null) {
            ps_employee.setInt(4, 0); //ps_employee is my PreparedStatement
        } else {
            ps_employee.setDate(4, birthdate);
        }

如果 post 字段没有值,birthday 为 null。否则生日包含一个有效的 java.sql.date 对象。

上面的代码有效,但它在 sql 中显示警告:“生日数据被截断”....

有没有更好的方法来插入 0000-00-00 日期???

感谢您的帮助..

【问题讨论】:

  • 否:# 软件:MySQL # 软件版本:5.5.25a - MySQL Community Server (GPL)
  • 您不应在数据库中存储无效日期。请改用 NULL。

标签: java mysql sql date


【解决方案1】:
  String query="INSERT INTO tablename values(?) "
  Connection connection = get the dbconn;
  PreparedStatement preparedStatement= connection.prepareStatement(query);      
  preparedStatement.setNull(0, java.sql.Types.DATE)

【讨论】:

  • 我已经试过了,但是它抛出了一个异常:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'Birthdate' cannot be null
  • @JannisHanke oh.. 你不能用简单的日期格式创建一个值为 0000-00-00 的日期并在生日为 null 时插入吗?
  • hm 我也试过这个:java.sql.Date.valueOf("0000-00-00");但它也会引发异常.. :(
  • @JuanMendes 会在哪一行抛出异常??
  • @JuanMendes 我猜他的意思是:java.sql.Date.valueOf("0000-00-00")
【解决方案2】:

如果提交的字段为空,您应该存储null,而不是某个在未来 20 年内使用该数据的每个应用程序都必须检查的神奇值。

在输出时将 null 转换为 '0000-00-00' - 或其他任何内容。

【讨论】:

  • 更改数据库架构。真的。
  • 这意味着您的数据库设计不符合您的业务需求。所以你有两个选择:第一,强制用户输入生日;其次,更改数据库以允许在生日列中使用空值。实际上,第一个选项会比第二个更痛苦,所以你应该实施第二个。
【解决方案3】:

您收到错误的原因是您试图插入无效的日期。没有 0 月和 0 日之类的东西。 MySQL 不会接受它作为日期。

要么使用其他用户描述的 NULL,要么使用您定义的某个最小基线日期,例如“1100-01-01”

【讨论】:

  • 我不能插入空值。这个值在mysql数据库字段“日期”中是不允许的,空值是0000-00-00
  • 在您的应用程序中将特殊值视为空然后,例如 1100-01-01。
  • 实际上 MySQL 接受无效日期(例如 2012-02-31 或 0000-00-00),但 JDBC 驱动程序稍后会拒绝加载它们,因为它们 无效的,你不能在Java中实例化一个无效的日期。
【解决方案4】:

解决方案取决于系统的数据完整性要求。

有两个(而且只有两个)选择:

  1. 系统的设计( 是一种设计,对吗?)要求每条记录都有一个有效的出生日期。在这种情况下,UI 有责任强制执行此操作。您的代码将永远不会收到空日期,如果收到,则有理由抛出异常。这消除了你的问题。
  2. 在某些情况下可以忽略生日。在这种情况下,必须更改表定义以允许空值。

您要做的最后一件事是编造一些特殊的日期值作为 NULL 的替代。该列不是 NULL 的事实表明上述情况 (1) 适用,并且您的特殊日期实际上违反了系统的数据完整性不变量。每个未来负责维护代码的程序员都会诅咒你的名字。

【讨论】:

    【解决方案5】:

    我会这样做的方式是通过列的默认值。根据类型将默认设置为“0000-00-00”或“0000-00-00 00:00:00”。

    一旦您在数据库中设置了默认值,您就无需在插入语句中提供该字段。您可以对不允许为空的列执行此操作,它仍将插入 MySQL 伪空日期。

    如果你这样做了,那么你也应该将此参数添加到你的 SQL 连接字符串中,因为你无法读取 zeroDate...

    zeroDateTimeBehavior=convertToNull

    最终结果是您将这些日期在 Java 中处理为 null,在 MySQL 数据库中处理为 0000-00-00。

    【讨论】:

      【解决方案6】:

      如果您的数据允许 NULL,但您的 SQL 表不允许(例如复合键的日期部分),您可以使用普遍接受的“空”值(GUI 工具使用此值)来表示默认/空白日期。这是一个例子:

      private static final String EMPTY_MYSQL_DATE = "0000-00-00 00:00:00";    
      ...
      if(someDateObj == null) {
          preparedStatement.setString(++psIndex, EMPTY_MYSQL_DATE);
      } else {
          preparedStatement.setTimestamp(++psIndex, new Timestamp(someDateObj.getTime()));
      }
      

      【讨论】:

        【解决方案7】:

        没有好的解决方案

        如何处理数据库中的空日期时间值的问题没有很好的解决方案。

        避免 NULL

        正如Dr. Chris DateA Guide to the SQL standard 和其他地方解释的那样,NULLs 是一场血腥的噩梦。一般来说,如果可能的话,应该避免它们。在 SQL 数据库中,这意味着将每一列声明为 NOT NULL(我希望有一种方法可以将其设为默认值)。在合理的情况下,我们设置了某种默认值。

        日期时间没有好的默认值

        但是为日期时间值设置默认值可能会非常令人困惑。 SQL 标准和 JDBC/java.sql.* 都没有将任何特定值定义为“空”标志。

        Java 8 及更高版本中内置的java.time 框架确实将最小值和最大值定义为InstantLocalDateTimeLocalDateLocalTime 类中的常量。这些值可能用作标志值,但解决的关键问题除外。这些 java.time 类解析为nanoseconds,而许多数据库具有更大的粒度,例如Postgres 中的microseconds。最小值和最大值非常极端,以至于当截断到更小的粒度时它们的含义会发生变化。

        零值

        您可能会直觉地认为考虑零值日期时间,0000-00-00 00:00:00.0 / 0000-00-00 / 00:00:00.0。没那么简单。

        没有零月/日

        首先,不存在00 的月份,也没有00 的月份。所以,好的,我们可以将其更改为 0000-01-01,即 1 月 1 日。

        没有零年

        下一期:没有0000 这样的年份。我不知道所有数据库,但至少在 Postgres 中没有这样的零年。尝试插入这样的值会产生错误。

        INSERT INTO moment_  -- TIMESTAMP WITH TIME ZONE.
        VALUES (  '0000-01-01 00:00:00.0Z' ) ;
        

        …抛出错误:

        ERROR:  date/time field value out of range: "0000-01-01 00:00:00.0Z"
        LINE 2: VALUES (  '0000-01-01 00:00:00.0Z' ) ;
                          ^
        ********** Error **********
        

        0000 更改为第一年0001 有效。

        INSERT INTO moment_  -- TIMESTAMP WITH TIME ZONE.
        VALUES (  '0001-01-01 00:00:00.0Z' ) ;
        
        SET TIME ZONE 'UTC' ;
        
        TABLE moment_ ;
        

        0001-01-01 00:00:00+00

        该数字是 BCAD 时代的支点。让我们减去一个小时来看看从 AD 移动到 BC 的行为。顺便说一句,BC 等字符串取决于区域设置,因此可能因您而异。另外请注意,我们必须将时区设置为 UTC 才能正确完成此日期时间操作。

        SET TIME ZONE 'UTC' ;
        
        INSERT INTO moment_  -- TIMESTAMP WITH TIME ZONE.
        VALUES ( TIMESTAMP '0001-01-01 00:00:00.0Z' - INTERVAL '1 hour' ) ;
        
        SET TIME ZONE 'UTC' ;
        
        TABLE moment_ ;
        

        结果是年份0001 BC,所以我们从0001跳转到0001 BC;没有零年0000

        “0001-12-31 23:00:00+00 BC”

        因此,如果您决定使用某个标志值,则不能使用真正的零值,但可以使用接近零的值,即公元第一年一月的第一天:'0001-01-01 00:00:00.0Z'

        时区

        使用此特殊标志值时,您必须注意时区。

        传递字符串时,会隐式应用 SQL 会话的当前默认时区(至少在 Postgres 中)。

        对 NULL 排序

        在我自己的工作中,我确实将几乎所有列都设为 NOT NULL 并设置了默认值。但是日期时间列是一个例外,因为没有可用的标志值。我已经为此受苦了。具体来说,我学会了如何使用NULLS FIRST/NULLS LAST 修饰符,因为SQL standard does not explicitly define a default sort order for NULL.

        【讨论】:

          猜你喜欢
          • 2020-06-08
          • 1970-01-01
          • 1970-01-01
          • 2020-03-21
          • 1970-01-01
          • 2017-11-21
          • 2011-03-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多