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

85

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

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)
Джейсон С
источник

Ответы:

85

Я наткнулся на это, пытаясь решить ту же проблему. В установке, с которой я работаю, используются JBOSS и Hibernate, поэтому мне пришлось сделать это по-другому. В базовом случае вы должны иметь возможность добавить zeroDateTimeBehavior=convertToNullк своему URI подключения в соответствии с этой страницей свойств конфигурации .

Я нашел другие предложения по всей стране, касающиеся включения этого параметра в конфигурацию гибернации:

В 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>

Надеюсь, это кому-то поможет. :)

Сарумон
источник
Внесение изменений в файл hibernate.cfg.xml
поможет
124

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

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

Редактировать:

Источник: MySQL Manual

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

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

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

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

Брайан Клозел
источник
интересно. где вы нашли эту информацию?
Джейсон С.
только что обновил свой ответ! Но URL-адрес @sarumont в этом случае может подойти лучше (в нем перечислены все свойства коннектора).
Брайан Клозел,
1
Версия 5.1.16 программного обеспечения jdbc содержит следующее исправление: - Исправление для ОШИБКИ № 57808 - wasNull не установлен для поля DATE со значением 0000-00-00 в getDate (), хотя zeroDateTimeBehavior имеет значение convertToNull.
Alexander Kjäll
Вот это да! Спасибо за информацию. Думаю, вам было трудно это понять :-(
Брайан Клозел
URL .. Жизнь. Сохранено!
CodingInCircles 05
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 сохраняет неявное значение по умолчанию для типа данных столбца.

Типы даты и времени MySQL 5.x :

MySQL также позволяет сохранять «0000-00-00» в качестве «фиктивной даты» (если вы не используете режим SQL NO_ZERO_DATE). В некоторых случаях это более удобно (и требует меньше данных и пространства индекса), чем использование значений 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, поскольку это позволяет вам явно форматировать его так, как вы хотите.
Jason 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 также объединяет несколько аргументов с URL-адресом базы данных, который они предоставляют пользователям, и это решение из-за того, что Heroku использует объединение "?" перед собственным набором аргументов работать не будет. Однако я нашел другое решение, которое, похоже, работает одинаково хорошо.

УСТАНОВИТЬ sql_mode = 'NO_ZERO_DATE';

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

Aqume
источник
3

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

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

Кстати:

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

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

Питер Лоури
источник
да, но до сих пор нет года, названного 0, даже ретроспективно. Год перед 1 г. н.э. / н.э. был 1 г. до н.э. / до н.э.
Питер Лоури,
2
Это вся причина, по которой MySQL использует 0000 как недопустимую дату, потому что она не конфликтует с существующими датами. Кроме того, даты вроде 1999-00-00 иногда используются (не мной!) Для обозначения 1999 года, а не конкретного дня.
Джейсон С.
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();
    }

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

EdU
источник
Я не думаю, что это решает проблему преобразования MySQL DATETIME всех нулей в действительную дату. Он просто показывает, как ввести null в поле DATETIME, что на самом деле не очень поможет.
Марк Чорли
2

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

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

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

Дамио
источник
2

вы можете добавить URL-адрес 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
Мукул Аггарвал
источник