Behandlung von DATETIME-Werten 0000-00-00 00:00:00 in JDBC

85

Ich bekomme eine Ausnahme (siehe unten), wenn ich es versuche

resultset.getString("add_date");

für eine JDBC-Verbindung zu einer MySQL-Datenbank mit einem DATETIME-Wert von 0000-00-00 00:00:00 (dem Quasi-Null-Wert für DATETIME), obwohl ich nur versuche, den Wert als Zeichenfolge und nicht als Zeichenfolge abzurufen Objekt.

Ich habe das umgangen

SELECT CAST(add_date AS CHAR) as add_date

was funktioniert, aber albern erscheint ... gibt es einen besseren Weg, dies zu tun?

Mein Punkt ist, dass ich nur die rohe DATETIME-Zeichenfolge möchte, damit ich sie selbst analysieren kann, wie sie ist .

Hinweis: Hier kommt der 0000 ins Spiel: (von http://dev.mysql.com/doc/refman/5.0/en/datetime.html )

Unzulässige DATETIME-, DATE- oder TIMESTAMP-Werte werden in den Wert „Null“ des entsprechenden Typs ('0000-00-00 00:00:00' oder '0000-00-00') konvertiert.

Die spezifische Ausnahme ist diese:

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)
Jason S.
quelle

Antworten:

85

Ich bin über diesen Versuch gestolpert, das gleiche Problem zu lösen. Die Installation, mit der ich arbeite, verwendet JBOSS und Hibernate, daher musste ich dies anders machen. Für den Basisfall sollten Sie in der Lage sein, zeroDateTimeBehavior=convertToNullIhren Verbindungs-URI gemäß dieser Konfigurationseigenschaftsseite hinzuzufügen .

Ich habe im ganzen Land andere Vorschläge gefunden, die sich darauf beziehen, diesen Parameter in Ihre Konfiguration für den Ruhezustand einzufügen:

In hibernate.cfg.xml :

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

In hibernate.properties :

hibernate.connection.zeroDateTimeBehavior=convertToNull

Aber ich musste es in meine mysql-ds.xml- Datei für JBOSS einfügen als:

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

Hoffe das hilft jemandem. :) :)

Sarumont
quelle
Dadurch könnte die Änderung auf der hibernate.cfg.xml Datei für mich den Trick tun, danke, weil ich fast Null Wissen auf Java
Gendrith
124

Alternative Antwort: Sie können diese JDBC-URL direkt in Ihrer Datenquellenkonfiguration verwenden:

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

Bearbeiten:

Quelle: MySQL-Handbuch

Datenzeiten mit All-Zero-Komponenten (0000-00-00 ...) - Diese Werte können in Java nicht zuverlässig dargestellt werden. Connector / J 3.0.x hat sie beim Lesen aus einem ResultSet immer in NULL konvertiert.

Connector / J 3.1 löst standardmäßig eine Ausnahme aus, wenn diese Werte auftreten, da dies das korrekteste Verhalten gemäß den JDBC- und SQL-Standards ist. Dieses Verhalten kann mithilfe der Konfigurationseigenschaft zeroDateTimeBehavior geändert werden. Die zulässigen Werte sind:

  • Ausnahme (Standardeinstellung), die eine SQLException mit einem SQLState von S1009 auslöst.
  • convertToNull , das anstelle des Datums NULL zurückgibt.
  • runde , die das Datum auf den nächsten Wert rundet, der 0001-01-01 ist.

Update: Alexander hat einen Fehler gemeldet, der mysql-connector-5.1.15 für diese Funktion betrifft. Siehe CHANGELOGS auf der offiziellen Website .

Brian Clozel
quelle
interessant. Wo hast du diese Informationen gefunden?
Jason S
habe gerade meine Antwort aktualisiert! In diesem Fall passt die URL von @sarumont möglicherweise besser (sie listet alle Connector-Eigenschaften auf).
Brian Clozel
1
Version 5.1.16 der jdbc-Software enthält diesen Bugfix: - Fix für BUG # 57808 - wasNull wurde nicht für das DATE-Feld mit dem Wert 0000-00-00 in getDate () festgelegt, obwohl zeroDateTimeBehavior convertToNull ist.
Alexander Kjäll
Beeindruckend! Danke für die Information. Ich denke, es fiel Ihnen schwer, das herauszufinden :-(
Brian Clozel
Die URL .. Leben. Gerettet!
CodingInCircles
11

Mein Punkt ist, dass ich nur die rohe DATETIME-Zeichenfolge möchte, damit ich sie selbst analysieren kann, wie sie ist.

Das lässt mich denken, dass Ihre "Problemumgehung" keine Problemumgehung ist, sondern tatsächlich die einzige Möglichkeit, den Wert aus der Datenbank in Ihren Code zu übernehmen:

SELECT CAST(add_date AS CHAR) as add_date

Übrigens noch ein paar Hinweise aus der MySQL-Dokumentation:

MySQL- Einschränkungen für ungültige Daten :

Vor MySQL 5.0.2 vergibt MySQL illegale oder unzulässige Datenwerte und zwingt sie zur Dateneingabe zu legalen Werten. In MySQL 5.0.2 und höher bleibt dies das Standardverhalten. Sie können jedoch den Server-SQL-Modus ändern, um eine traditionellere Behandlung fehlerhafter Werte auszuwählen, sodass der Server sie ablehnt und die Anweisung abbricht, in der sie auftreten.

[..]

Wenn Sie versuchen, NULL in einer Spalte zu speichern, die keine NULL-Werte akzeptiert, tritt bei einzeiligen INSERT-Anweisungen ein Fehler auf. Bei mehrzeiligen INSERT-Anweisungen oder bei INSERT INTO ... SELECT-Anweisungen speichert MySQL Server den impliziten Standardwert für den Spaltendatentyp.

MySQL 5.x Datums- und Uhrzeittypen :

Mit MySQL können Sie auch '0000-00-00' als "Dummy-Datum" speichern (wenn Sie den SQL-Modus NO_ZERO_DATE nicht verwenden). Dies ist in einigen Fällen bequemer (und benötigt weniger Daten und Indexraum) als die Verwendung von NULL-Werten.

[..]

Wenn MySQL auf einen Wert für einen Datums- oder Zeittyp stößt, der außerhalb des Bereichs liegt oder für den Typ anderweitig unzulässig ist (wie am Anfang dieses Abschnitts beschrieben), wird der Wert für diesen Typ in den Wert „Null“ konvertiert.

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

Verwenden Sie dies, um den Fehler zu vermeiden. Es gibt das Datum im Zeichenfolgenformat zurück und kann dann als Zeichenfolge abgerufen werden.

resultset.getString("dtime");

Das funktioniert eigentlich NICHT. Auch wenn Sie getString aufrufen. Intern versucht MySQL immer noch, es zuerst auf das Datum zu konvertieren.

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

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

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

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

Gemeinschaft
quelle
1
Das ist ein bisschen das gleiche wie bei meiner CAST-Lösung (add_date AS CHAR), aber +1, da Sie es so explizit so formatieren können, wie Sie es möchten.
Jason S
5

Wenn nach dem Hinzufügen von Zeilen:

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

hibernate.connection.zeroDateTimeBehavior=convertToNull

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

bleibt weiterhin ein Fehler:

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').

Zeilen finden:

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

durch folgende Zeilen ersetzen:

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

Ich habe mit diesem Problem gerungen und die oben diskutierten 'convertToNull'-Lösungen implementiert. Es hat in meiner lokalen MySql-Instanz funktioniert. Aber wenn ich meine Play / Scala-App für Heroku bereitstellte, funktionierte sie nicht mehr. Heroku verkettet auch mehrere Argumente mit der DB-URL, die sie Benutzern und dieser Lösung zur Verfügung stellen, da Heroku die Verkettung von "?" vor ihren eigenen Argumenten wird nicht funktionieren. Ich habe jedoch eine andere Lösung gefunden, die genauso gut zu funktionieren scheint.

SET sql_mode = 'NO_ZERO_DATE';

Ich habe dies in meine Tabellenbeschreibungen aufgenommen und es hat das Problem gelöst, dass '0000-00-00 00:00:00' nicht als java.sql.Timestamp dargestellt werden kann

Aqume
quelle
3

Ich schlage vor, Sie verwenden null, um einen Nullwert darzustellen.

Was ist die Ausnahme, die Sie bekommen?

Übrigens:

Es gibt kein Jahr mit dem Namen 0 oder 0000. (Obwohl einige Daten dieses Jahr zulassen)

Und es gibt keinen 0 Monat des Jahres oder 0 Tag des Monats. (Was kann die Ursache Ihres Problems sein)

Peter Lawrey
quelle
Ja, aber es gibt immer noch kein Jahr mit dem Namen 0, auch nicht nachträglich. Das Jahr vor 1 AD / CE war 1 BC / BCE
Peter Lawrey
2
Das ist der ganze Grund, warum MySQL 0000 als ungültiges Datum verwendet, da es nicht mit vorhandenen Daten in Konflikt steht. Außerdem werden Daten wie 1999-00-00 manchmal (nicht von mir!) Verwendet, um das Jahr 1999 und nicht einen bestimmten Tag darzustellen.
Jason S
3

Ich habe das Problem gelöst, da '00 -00 -.... 'kein gültiges Datum ist. Dann habe ich meine SQL-Spaltendefinition geändert und den Ausdruck "NULL" hinzugefügt, um Nullwerte zuzulassen:

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),...

Dann muss ich in der Lage sein, NULL-Werte einzufügen, das heißt "Ich habe diesen Zeitstempel noch nicht registriert" ...

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),...

Die Tabelle sieht so aus:

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)

Endlich: JPA in Aktion:

@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();
    }

Endlich seine ganze Arbeit. Ich hoffe das hilft dir.

EdU
quelle
Ich glaube nicht, dass dies das Problem der Konvertierung einer MySQL-DATETIME aller Nullen in ein gültiges Datum angeht. Es zeigt nur, wie man null in ein DATETIME-Feld eingibt, was nicht wirklich viel hilft.
Mark Chorley
2

So fügen Sie die anderen Antworten hinzu: Wenn Sie die 0000-00-00Zeichenfolge möchten , können Sie sie verwenden noDatetimeStringSync=true(mit der Einschränkung, die Zeitzonenumwandlung zu opfern).

Der offizielle MySQL-Fehler: https://bugs.mysql.com/bug.php?id=47108 .

Auch für Geschichte, verwendet JDBC zurückzukehren NULLfür 0000-00-00Termine , aber jetzt eine Ausnahme von Standard zurück. Quelle

damio
quelle
2

Sie können die JDBC-URL mit anhängen

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

Mit dieser Hilfe konvertiert SQL '0000-00-00 00:00:00' als Nullwert.

z.B:

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