在JDBC中处理DATETIME值0000-00-00 00:00:00


85

如果尝试这样做,我会得到一个例外(见下文)

resultset.getString("add_date");

用于与包含注册日期为00:00:00的DATETIME值(DATETIME的准空值)的MySQL数据库的JDBC连接,即使我只是试图将值作为字符串而不是目的。

我通过这样做解决了

SELECT CAST(add_date AS CHAR) as add_date

哪个可行,但看起来很愚蠢……有更好的方法吗?

我的意思是,我只是想生DATETIME字符串,所以我可以解析它自己为是

注意:这里是0000的来源:(来自http://dev.mysql.com/doc/refman/5.0/en/datetime.html

非法的DATETIME,DATE或TIMESTAMP值将转换为适当类型(“ 0000-00-00 00:00:00”或“ 0000-00-00”)的“零”值。

具体的例外情况是:

SQLException: Cannot convert value '0000-00-00 00:00:00' from column 5 to TIMESTAMP.
SQLState: S1009
VendorError: 0
java.sql.SQLException: Cannot convert value '0000-00-00 00:00:00' from column 5 to TIMESTAMP.
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:926)
    at com.mysql.jdbc.ResultSetImpl.getTimestampFromString(ResultSetImpl.java:6343)
    at com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.java:5670)
    at com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5491)
    at com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5531)

Answers:


85

我偶然发现了这个尝试解决相同问题的方法。我正在使用的安装使用JBOSS和Hibernate,因此我不得不以不同的方式进行操作。对于基本情况,您应该能够zeroDateTimeBehavior=convertToNull根据此配置属性页面添加到连接URI 。

我在全国范围内发现了其他建议,这些建议涉及将该参数放入您的休眠配置中:

hibernate.cfg.xml中

<property name="hibernate.connection.zeroDateTimeBehavior">convertToNull</property>

hibernate.properties中

hibernate.connection.zeroDateTimeBehavior=convertToNull

但是我必须将其放入JBOSS的mysql-ds.xml文件中,如下所示:

<connection-property name="zeroDateTimeBehavior">convertToNull</connection-property>

希望这对某人有帮助。:)


谢谢,因为在hibernate.cfg.xml文件上进行更改对我来说很成功,因为我对Java的知识几乎为零
Gendrith

124

替代答案,您可以在数据源配置中直接使用此JDBC URL:

jdbc:mysql://yourserver:3306/yourdatabase?zeroDateTimeBehavior=convertToNull

编辑:

资料来源:MySQL手册

具有全零成分(0000-00-00 ...)的日期时间-这些值不能在Java中可靠地表示。从ResultSet读取时,Connector / J 3.0.x始终将它们转换为NULL。

默认情况下,当遇到这些值时,Connector / J 3.1会引发异常,因为根据JDBC和SQL标准,这是最正确的行为。可以使用zeroDateTimeBehavior配置属性来修改此行为。允许的值为:

  • 异常(默认),该异常将引发SQLState为S1009的SQLException。
  • convertToNull,它返回NULL而不是日期。
  • round,将日期四舍五入到最接近的最近值0001-01-01。

更新:Alexander报告了一个影响mysql-connector-5.1.15的错误。请参阅官方网站上的CHANGELOGS


有趣。您在哪里找到此信息?
杰森S

刚刚更新了我的答案!但是@sarumont的URL在这种情况下可能会更好(它列出了所有连接器属性)。
Brian Clozel

1
jdbc软件的5.1.16版包含以下错误修正:-修正了BUG#57808-尽管zeroDateTimeBehavior是convertToNull,但未为DATE字段设置getDate()中的值为0000-00-00的wasNull。
亚历山大·贾尔

哇!谢谢(你的)信息。我想您很难解决这个问题:-(
Brian Clozel 2011年

URL ..生活。保存了!
CodingInCircles 2013年

11

我的观点是,我只想要原始的DATETIME字符串,因此我可以自己解析它。

这让我认为您的“解决方法”不是解决方法,而是实际上将数据库中的值转换为代码的唯一方法:

SELECT CAST(add_date AS CHAR) as add_date

顺便说一句,MySQL文档提供了更多注意事项:

MySQL对无效数据的约束

在MySQL 5.0.2之前,MySQL会容忍非法或不正确的数据值,并将其强制为合法值以进行数据输入。在MySQL 5.0.2及更高版本中,这仍然是默认行为,但是您可以更改服务器SQL模式以选择更传统的坏值处理方式,以便服务器拒绝它们并中止出现它们的语句。

[..]

如果尝试将NULL存储到不采用NULL值的列中,则单行INSERT语句会发生错误。对于多行INSERT语句或INSERT INTO ... SELECT语句,MySQL Server存储列数据类型的隐式默认值。

MySQL 5.x日期和时间类型

MySQL还允许您将“ 0000-00-00”存储为“虚拟日期”(如果您未使用NO_ZERO_DATE SQL模式)。在某些情况下,这比使用NULL值更方便(并且使用更少的数据和索引空间)。

[..]

默认情况下,当MySQL遇到日期或时间类型的值超出范围或对该类型非法(如本节开头所述)时,它将将该值转换为该类型的“零”值。


6
DATE_FORMAT(column name, '%Y-%m-%d %T') as dtime

使用它来避免错误。它以字符串格式返回日期,然后您可以将其作为字符串获取。

resultset.getString("dtime");

这实际上是行不通的。即使您调用getString。在内部,mysql仍然尝试首先将其转换为日期。

在com.mysql.jdbc.ResultSetImpl.getDateFromString(ResultSetImpl.java:2270)

〜[mysql-connector-java-5.1.15.jar:na]在com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.java:5743)

〜[mysql-connector-java-5.1.15.jar:na]在com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5576)

〜[mysql-connector-java-5.1.15.jar:na]


1
有点和我的CAST(add_date AS CHAR)解决方案相同,但是+1是因为这样可以使您明确地格式化所需的格式。
杰森S

5

如果在添加行之后:

<property
name="hibernate.connection.zeroDateTimeBehavior">convertToNull</property>

hibernate.connection.zeroDateTimeBehavior=convertToNull

<connection-property
name="zeroDateTimeBehavior">convertToNull</connection-property>

仍然是一个错误:

Illegal DATETIME, DATE, or TIMESTAMP values are converted to the “zero” value of the appropriate type ('0000-00-00 00:00:00' or '0000-00-00').

查找行:

1) resultSet.getTime("time"); // time = 00:00:00
2) resultSet.getTimestamp("timestamp"); // timestamp = 00000000000000
3) resultSet.getDate("date"); // date = 0000-00-00 00:00:00

分别替换为以下几行:

1) Time.valueOf(resultSet.getString("time"));
2) Timestamp.valueOf(resultSet.getString("timestamp"));
3) Date.valueOf(resultSet.getString("date"));

5

我努力解决了这个问题,并实现了上面讨论的“ convertToNull”解决方案。它在我的本地MySql实例中工作。但是,当我将Play / Scala应用程序部署到Heroku时,它将不再起作用。Heroku还将几个args串联到它们提供给用户的DB URL上,并且由于Heroku使用“?”串联,因此此解决方案也是如此。在自己设置参数之前,将无法正常工作。但是,我发现了另一种似乎同样有效的解决方案。

SET sql_mode ='NO_ZERO_DATE';

我将其放在表描述中,它解决了无法将“ 0000-00-00 00:00:00”表示为java.sql.Timestamp的问题


3

我建议您使用null表示空值。

你有什么例外?

顺便说一句:

没有称为0或0000的年份。(尽管某些日期允许今年)

并且没有一年的0个月或每月的0天。(这可能是您造成问题的原因)


是的,但是即使追溯地,也没有年份称为0。公元前1年的前一年是公元前1年
彼得·劳瑞2009年

2
这就是MySQL使用0000作为无效日期的全部原因,因为它与现有日期不冲突。另外,有时使用1999-00-00之类的日期(不是我自己!)来表示1999年,而不是特定的日期。
杰森S

3

我解决了考虑“ 00-00 -....”不是有效日期的问题,然后,我更改了SQL列定义,添加了“ NULL”表达式以允许使用空值:

SELECT "-- Tabla item_pedido";
CREATE TABLE item_pedido (
    id INTEGER AUTO_INCREMENT PRIMARY KEY,
    id_pedido INTEGER,
    id_item_carta INTEGER,
    observacion VARCHAR(64),
    fecha_estimada TIMESTAMP,
    fecha_entrega TIMESTAMP NULL, // HERE IS!!.. NULL = DELIVERY DATE NOT SET YET
    CONSTRAINT fk_item_pedido_id_pedido FOREIGN KEY (id_pedido)
        REFERENCES pedido(id),...

然后,我必须能够插入NULL值,这意味着“我尚未注册该时间戳”。

SELECT "++ INSERT item_pedido";
INSERT INTO item_pedido VALUES
(01, 01, 01, 'Ninguna', ADDDATE(@HOY, INTERVAL 5 MINUTE), NULL),
(02, 01, 02, 'Ninguna', ADDDATE(@HOY, INTERVAL 3 MINUTE), NULL),...

表格看起来:

mysql> select * from item_pedido;
+----+-----------+---------------+-------------+---------------------+---------------------+
| id | id_pedido | id_item_carta | observacion | fecha_estimada      | fecha_entrega       |
+----+-----------+---------------+-------------+---------------------+---------------------+
|  1 |         1 |             1 | Ninguna     | 2013-05-19 15:09:48 | NULL                |
|  2 |         1 |             2 | Ninguna     | 2013-05-19 15:07:48 | NULL                |
|  3 |         1 |             3 | Ninguna     | 2013-05-19 15:24:48 | NULL                |
|  4 |         1 |             6 | Ninguna     | 2013-05-19 15:06:48 | NULL                |
|  5 |         2 |             4 | Suave       | 2013-05-19 15:07:48 | 2013-05-19 15:09:48 |
|  6 |         2 |             5 | Seco        | 2013-05-19 15:07:48 | 2013-05-19 15:12:48 |
|  7 |         3 |             5 | Con Mayo    | 2013-05-19 14:54:48 | NULL                |
|  8 |         3 |             6 | Bilz        | 2013-05-19 14:57:48 | NULL                |
+----+-----------+---------------+-------------+---------------------+---------------------+
8 rows in set (0.00 sec)

最后:JPA的实际应用:

@Stateless
@LocalBean
public class PedidosServices {
    @PersistenceContext(unitName="vagonpubPU")
    private EntityManager em;

    private Logger log = Logger.getLogger(PedidosServices.class.getName());

    @SuppressWarnings("unchecked")
    public List<ItemPedido> obtenerPedidosRetrasados() {
        log.info("Obteniendo listado de pedidos retrasados");
        Query qry = em.createQuery("SELECT ip FROM ItemPedido ip, Pedido p WHERE" +
                " ip.fechaEntrega=NULL" +
                " AND ip.idPedido=p.id" +
                " AND ip.fechaEstimada < :arg3" +
                " AND (p.idTipoEstado=:arg0 OR p.idTipoEstado=:arg1 OR p.idTipoEstado=:arg2)");
        qry.setParameter("arg0", Tipo.ESTADO_BOUCHER_ESPERA_PAGO);
        qry.setParameter("arg1", Tipo.ESTADO_BOUCHER_EN_SERVICIO);
        qry.setParameter("arg2", Tipo.ESTADO_BOUCHER_RECIBIDO);
        qry.setParameter("arg3", new Date());

        return qry.getResultList();
    }

终于完成了所有工作。希望对您有所帮助。


我不认为这解决了将全零的MySQL DATETIME转换为有效日期的问题。它仅显示了如何在DATETIME字段中输入null,这实际上并没有多大帮助。
马克·乔利

2

要添加到其他答案:如果0000-00-00您想要字符串,则可以使用noDatetimeStringSync=true(牺牲时区转换的警告)。

官方的MySQL错误:https : //bugs.mysql.com/bug.php?id=47108

此外,对于历史,JDBC用于返回NULL0000-00-00日期,但现在默认返回异常。 资源


2

您可以将jdbc网址附加到

?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8

借助于此,sql将“ 0000-00-00 00:00:00”转换为空值。

例如:

jdbc:mysql:<host-name>/<db-name>?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.