Обработка значений DATETIME 0000-00-00 00:00:00 в JDBC

Я получаю исключение (см. ниже), если я пытаюсь сделать

resultset.getString("add_date");

для соединения JDBC с базой данных MySQL, содержащей значение DATETIME 0000-00-00 00:00:00 (значение квазипучки для DATETIME), хотя я просто пытаюсь получить значение как строку, а не как объект.

Я обошел это, сделав

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)

Ответ 1

Я наткнулся на это, пытаясь решить ту же проблему. Установка, с которой я работаю, использует JBOSS и Hibernate, поэтому мне пришлось делать это по-другому. В базовом случае вы должны добавить zeroDateTimeBehavior=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

Альтернативный ответ, вы можете использовать этот URL JDBC непосредственно в вашей конфигурации источника данных:

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

Edit:

Источник: Руководство по MySQL

Datetimes с all-zero компонентами (0000-00-00...) - Эти значения не могут быть надежно представлены в Java. Connector/J 3.0.x всегда преобразовывал их в NULL при чтении из ResultSet.

Коннектор/J 3.1 генерирует исключение по умолчанию, когда эти значения встречаются, поскольку это наиболее правильное поведение в соответствии со стандартами JDBC и SQL. Это поведение можно изменить с помощью свойства конфигурации zeroDateTimeBehavior. Допустимые значения:

  • исключение (по умолчанию), которое генерирует SQLException с SQLState из S1009.
  • convertToNull, который возвращает NULL вместо даты.
  • круглый, который округляет дату до ближайшего ближайшего значения, которое составляет 0001-01-01.

Обновление: Александр сообщил об ошибке, влияющей на mysql-connector-5.1.15 на эту функцию. См. CHANGELOGS на официальном сайте.

Ответ 3

Моя точка зрения заключается в том, что мне просто нужна строка 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 встречает значение для даты или типа времени, которое выходит за пределы допустимого диапазона или иным образом незаконно для типа (как описано в начале этого раздела), оно преобразует значение в значение "нуль" для этот тип.

Ответ 4

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] at 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-разъем-Java-5.1.15.jar: на]

Ответ 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"));

Ответ 6

Я боролся с этой проблемой и реализовал решения "convertToNull", рассмотренные выше. Он работал в моем локальном экземпляре MySql. Но когда я развернул приложение Play/ Scala для Heroku, он больше не будет работать. Heroku также объединяет несколько аргументов с URL-адресом DB, который они предоставляют пользователям, и это решение, поскольку Heroku использует конкатенацию "?" до их собственного набора аргументов, не будет работать. Однако я нашел другое решение, которое, похоже, работает одинаково хорошо.

SET sql_mode = 'NO_ZERO_DATE';

Я помещал это в описания моих таблиц, и он решил проблему "0000-00-00 00:00:00" не может быть представлен как java.sql.Timestamp

Ответ 7

Я предлагаю вам использовать null для представления нулевого значения.

Какое исключение вы получаете?

BTW:

Нет года, называемого 0 или 0000. (Хотя некоторые даты разрешены в этом году)

И нет 0 месяца года или 0 дня месяца. (Это может быть причиной вашей проблемы)

Ответ 8

Я решил, что проблема с '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();
    }

Наконец, вся его работа. Надеюсь, это поможет вам.

Ответ 9

Чтобы добавить к другим ответам: Если вам нужна строка 0000-00-00, вы можете использовать noDatetimeStringSync=true (с предостережением жертвовать часовым поясом).

Официальная ошибка MySQL: https://bugs.mysql.com/bug.php?id=47108.

Кроме того, для истории JDBC используется для возврата NULL для 0000-00-00 дат, но теперь возвращает исключение по умолчанию. Источник

Ответ 10

вы можете добавить URL-адрес jdbc с помощью

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

С помощью этого, sql convert '0000-00-00 00:00:00' в качестве нулевого значения.

например:

jdbc:mysql:<host-name>/<db-name>?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8