обробка значень 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 роблять для мене фокус, дякую, бо я майже не маю знань про Java
Gendrith

124

Альтернативна відповідь, ви можете використовувати цю URL-адресу JDBC безпосередньо у конфігурації джерела даних:

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

Редагувати:

Джерело: Посібник MySQL

Дати з абсолютно нульовими компонентами (0000-00-00 ...) - Ці значення не можуть бути надійно представлені в Java. Connector / J 3.0.x завжди перетворював їх у NULL при зчитуванні з ResultSet.

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

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

Оновлення: Олександр повідомив про помилку, яка впливає на mysql-connector-5.1.15 щодо цієї функції. Див. CHANGELOGS на офіційному веб-сайті .


цікаво. де ти знайшов цю інформацію?
Jason S

просто оновив мою відповідь! Але в цьому випадку URL-адреса @sarumont може краще відповідати (вона містить усі властивості з'єднувача).
Брайан Клозел,

1
версія 5.1.16 програмного забезпечення jdbc містить таке виправлення: - Виправлено помилку № 57808 - wasNull не встановлено для поля DATE зі значенням 0000-00-00 у getDate (), хоча zeroDateTimeBehavior є convertToNull.
Alexander Kjäll

Оце Так! Дякую за інформацію. Думаю, вам було важко це зрозуміти :-(
Брайан Клозел

URL-адреса .. Життя. Збережено!
CodingInCircles

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" як "фіктивну дату" (якщо ви не використовуєте режим 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-конектор-java-5.1.15.jar: na]


1
Це приблизно так само, як моє рішення CAST (add_date AS CHAR), але +1, оскільки це дозволяє вам відформатувати його явно так, як ви хочете.
Джейсон С,

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


3

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

Який виняток ви отримуєте?

ДО:

Немає року, який називається 0 або 0000. (Хоча деякі дати дозволяють цей рік)

І немає 0 місяця в році або 0 числа місяця. (Що може бути причиною вашої проблеми)


так, але досі немає року, який називається 0, навіть ретроспективно. За рік до 1 р. Н. Е. Був 1 до н. Е. / До н. Е.
Пітер Лорі,

2
Це вся причина, чому MySQL використовує 0000 як недійсну дату, оскільки вона не суперечить існуючим датам. Крім того, такі дати, як 1999-00-00, іноді використовуються (не мною!) Для подання 1999 року, а не конкретного дня.
Jason 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 усіх нулів у дійсну дату. Це просто показує, як ввести 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.