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

时间:2009-04-23 17:45:16

标签: java sql date jdbc

如果我尝试

,我会遇到异常(见下文)
resultset.getString("add_date");

对于包含DATETIME值为00:00:00(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   值转换为“零”   适当类型的值   ('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)

10 个答案:

答案 0 :(得分:120)

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

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

编辑:

来源:MySQL Manual

  

使用全零组件的日期时间(0000-00-00) - 这些值无法在Java中可靠地表示。从ResultSet读取时,Connector / J 3.0.x始终将它们转换为NULL。

     

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

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

更新:亚历山大在该功能上报告了一个影响mysql-connector-5.1.15的错误。请参阅CHANGELOGS on the official website

答案 1 :(得分:81)

我偶然发现了这个试图解决同样问题的事情。我正在使用的安装使用JBOSS和Hibernate,所以我必须采用不同的方式。对于基本情况,您应该可以根据此configuration properties pagezeroDateTimeBehavior=convertToNull添加到您的连接URI。

我发现其他建议涉及将该参数放入你的hibernate配置中:

hibernate.cfg.xml

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

hibernate.properties

hibernate.connection.zeroDateTimeBehavior=convertToNull

但是我必须把它放在我的 mysql-ds.xml 文件中,用于JBOSS:

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

希望这有助于某人。 :)

答案 2 :(得分:11)

  

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

这让我觉得你的“解决方法”不是一种解决方法,但实际上是从数据库中获取代码的唯一方法:

SELECT CAST(add_date AS CHAR) as add_date

顺便提一下,MySQL文档中还有一些注释:

MySQL Constraints on Invalid Data

  

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

     

[..]

     

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

MySQL 5.x Date and Time Types

  

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

     

[..]

     

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

答案 3 :(得分:6)

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

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

resultset.getString("dtime");

这实际上不起作用。即使你调用getString。内部mysql仍然首先尝试将其转换为日期。

  

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

     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]

     

〜[MySQL的连接器的Java-5.1.15.jar:NA]

答案 4 :(得分: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 :(得分:5)

我解决了这个问题并实现了上面讨论的'convertToNull'解决方案。它在我的本地MySql实例中工作。但是当我将我的Pl​​ay / Scala应用程序部署到Heroku时,它不再有效。 Heroku还将几个args连接到他们为用户提供的DB URL,这个解决方案,因为Heroku使用了串联“?”在他们自己的一套args之前,将无法正常工作。然而,我发现了一种似乎同样有效的不同解决方案。

SET sql_mode ='NO_ZERO_DATE';

我把它放在我的表格描述中它解决了'0000-00-00 00:00:00'的问题不能表示为java.sql.Timestamp

答案 6 :(得分:3)

我建议你用null来表示一个空值。

你得到的例外是什么?

BTW:

没有一年被称为0或0000.(虽然有些日期允许今年)

一年中没有0个月或0个月。 (这可能是导致您出现问题的原因)

答案 7 :(得分: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();
    }

最后所有的工作。我希望能帮助你。

答案 8 :(得分:2)

要添加其他答案:如果您需要noDatetimeStringSync=true字符串,则可以使用NULL(但需要注意牺牲时区转换)。

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

此外,对于历史记录,JDBC过去常常为0000-00-00日期返回$modal.open({ templateUrl: filepath, controller: controllerName, scope: $scope }) .result.then(function () { alert('closed'); }, function () { alert('canceled'); }); ,但现在默认返回异常。 Source

答案 9 :(得分: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