gestione dei valori DATETIME 0000-00-00 00:00:00 in JDBC


85

Ottengo un'eccezione (vedi sotto) se provo a farlo

resultset.getString("add_date");

per una connessione JDBC a un database MySQL contenente un valore DATETIME di 0000-00-00 00:00:00 (il valore quasi nullo per DATETIME), anche se sto solo cercando di ottenere il valore come stringa, non come oggetto.

Ho aggirato questo problema facendo

SELECT CAST(add_date AS CHAR) as add_date

che funziona, ma sembra sciocco ... c'è un modo migliore per farlo?

Il punto è che voglio solo la stringa DATETIME non elaborata, quindi posso analizzarla da solo così com'è .

nota: ecco dove arriva 0000: (da http://dev.mysql.com/doc/refman/5.0/en/datetime.html )

I valori DATETIME, DATE o TIMESTAMP illegali vengono convertiti nel valore "zero" del tipo appropriato ("0000-00-00 00:00:00" o "0000-00-00").

L'eccezione specifica è questa:

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)

Risposte:


85

Mi sono imbattuto in questo tentativo di risolvere lo stesso problema. L'installazione con cui sto lavorando utilizza JBOSS e Hibernate, quindi ho dovuto farlo in un modo diverso. Per il caso di base, dovresti essere in grado di aggiungere zeroDateTimeBehavior=convertToNullall'URI di connessione come da questa pagina delle proprietà di configurazione .

Ho trovato altri suggerimenti in tutto il paese che si riferiscono a mettere quel parametro nella tua configurazione di ibernazione:

In hibernate.cfg.xml :

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

In hibernate.properties :

hibernate.connection.zeroDateTimeBehavior=convertToNull

Ma ho dovuto metterlo nel mio file mysql-ds.xml per JBOSS come:

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

Spero che questo aiuti qualcuno. :)


Fare la modifica sul file hibernate.cfg.xml fa il trucco per me, grazie, perché non ho quasi nessuna conoscenza su Java
Gendrith

124

Risposta alternativa, puoi utilizzare questo URL JDBC direttamente nella configurazione dell'origine dati:

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

Modificare:

Fonte: manuale MySQL

Datetimes with all-zero components (0000-00-00 ...) - Questi valori non possono essere rappresentati in modo affidabile in Java. Connector / J 3.0.x li converte sempre in NULL quando vengono letti da un ResultSet.

Connector / J 3.1 genera un'eccezione per impostazione predefinita quando vengono rilevati questi valori poiché questo è il comportamento più corretto in base agli standard JDBC e SQL. Questo comportamento può essere modificato utilizzando la proprietà di configurazione zeroDateTimeBehavior. I valori consentiti sono:

  • eccezione (l'impostazione predefinita), che genera una SQLException con SQLState di S1009.
  • convertToNull , che restituisce NULL invece della data.
  • round , che arrotonda la data al valore più vicino più vicino che è 0001-01-01.

Aggiornamento: Alexander ha segnalato un bug che interessava mysql-connector-5.1.15 su quella funzione. Vedi CHANGELOGS sul sito ufficiale .


interessante. dove hai trovato queste informazioni?
Jason S

ho appena aggiornato la mia risposta! Ma l'URL di @sarumont potrebbe adattarsi meglio in questo caso (elenca tutte le proprietà del connettore).
Brian Clozel

1
la versione 5.1.16 del software jdbc contiene questo bugfix: - Correzione per BUG # 57808 - wasNull non impostato per il campo DATE con valore 0000-00-00 in getDate () sebbene zeroDateTimeBehavior sia convertToNull.
Alexander Kjäll

Wow! Grazie per le informazioni. Immagino che tu abbia avuto difficoltà a
capirlo

11

Il punto è che voglio solo la stringa DATETIME non elaborata, quindi posso analizzarla da solo così com'è.

Questo mi fa pensare che la tua "soluzione alternativa" non sia una soluzione alternativa, ma in realtà l'unico modo per ottenere il valore dal database nel tuo codice:

SELECT CAST(add_date AS CHAR) as add_date

A proposito, altre note dalla documentazione di MySQL:

Vincoli di MySQL su dati non validi :

Prima di MySQL 5.0.2, MySQL perdona valori di dati illegali o impropri e li costringe a valori legali per l'immissione dei dati. In MySQL 5.0.2 e versioni successive, questo rimane il comportamento predefinito, ma è possibile modificare la modalità SQL del server per selezionare un trattamento più tradizionale dei valori errati in modo tale che il server li rifiuti e annulli l'istruzione in cui si verificano.

[..]

Se si tenta di memorizzare NULL in una colonna che non accetta valori NULL, si verifica un errore per le istruzioni INSERT a riga singola. Per le istruzioni INSERT su più righe o per le istruzioni INSERT INTO ... SELECT, MySQL Server memorizza il valore predefinito implicito per il tipo di dati della colonna.

Tipi di data e ora di MySQL 5.x :

MySQL consente anche di memorizzare "0000-00-00" come una "data fittizia" (se non si utilizza la modalità SQL NO_ZERO_DATE). Questo in alcuni casi è più conveniente (e utilizza meno dati e spazio di indice) rispetto all'utilizzo di valori NULL.

[..]

Per impostazione predefinita, quando MySQL rileva un valore per un tipo di data o ora che è fuori intervallo o altrimenti illegale per il tipo (come descritto all'inizio di questa sezione), converte il valore nel valore "zero" per quel tipo.


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

Usalo per evitare l'errore. Restituisce la data in formato stringa e quindi puoi ottenerla come stringa.

resultset.getString("dtime");

Questo in realtà NON funziona. Anche se chiami getString. Internamente mysql cerca ancora di convertirlo per primo.

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

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

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

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


1
È un po 'lo stesso della mia soluzione CAST (add_date AS CHAR), ma +1 poiché questo ti consente di formattarlo esplicitamente nel modo desiderato.
Jason S

5

Se, dopo aver aggiunto le righe:

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

hibernate.connection.zeroDateTimeBehavior=convertToNull

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

continua ad essere un errore:

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

trova linee:

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

sostituire con le seguenti righe, rispettivamente:

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

5

Ho lottato con questo problema e ho implementato le soluzioni "convertToNull" discusse sopra. Ha funzionato nella mia istanza MySql locale. Ma quando ho distribuito la mia app Play / Scala su Heroku, non funzionava più. Heroku concatena anche diversi argomenti all'URL DB che forniscono agli utenti e questa soluzione, a causa dell'utilizzo da parte di Heroku della concatenazione di "?" prima del proprio set di argomenti, non funzionerà. Tuttavia ho trovato una soluzione diversa che sembra funzionare altrettanto bene.

SET sql_mode = 'NO_ZERO_DATE';

L'ho inserito nelle descrizioni delle tabelle e ha risolto il problema di "0000-00-00 00:00:00" non può essere rappresentato come java.sql.Timestamp


3

Ti suggerisco di usare null per rappresentare un valore null.

Qual è l'eccezione che ottieni?

BTW:

Non esiste un anno chiamato 0 o 0000 (anche se alcune date lo consentono quest'anno)

E non ci sono 0 mesi dell'anno o 0 giorni del mese. (Che potrebbe essere la causa del tuo problema)


sì, ma non c'è ancora nessun anno chiamato 0, anche retrospettivamente. L'anno prima dell'1 d.C. / d.C. era il primo a.C. / a.C.
Peter Lawrey,

2
Questa è l'intera ragione per cui MySQL utilizza 0000 come data non valida, perché non è in conflitto con le date esistenti. Inoltre, date come 1999-00-00 vengono talvolta usate (non da me!) Per rappresentare l'anno 1999 piuttosto che un giorno specifico.
Jason S

3

Ho risolto il problema considerando che '00 -00 -.... 'non è una data valida, quindi ho modificato la definizione della mia colonna SQL aggiungendo l'espressione "NULL" per consentire valori nulli:

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

Quindi, devo essere in grado di inserire valori NULL, il che significa "non ho ancora registrato quel timestamp" ...

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

Il tavolo sembra che:

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)

Infine: JPA in azione:

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

Finalmente tutto il suo lavoro. Spero che ti aiuti.


Non penso che questo risolva il problema della conversione di un DATETIME MySQL di tutti zeri in una data valida. Mostra solo come inserire null in un campo DATETIME che non aiuterà molto.
Mark Chorley

2

Da aggiungere alle altre risposte: Se vuoi la 0000-00-00stringa, puoi usare noDatetimeStringSync=true(con l'avvertenza di sacrificare la conversione del fuso orario).

Il bug ufficiale di MySQL: https://bugs.mysql.com/bug.php?id=47108 .

Inoltre, per la storia, JDBC utilizzata per restituire NULLper 0000-00-00le date, ma ora restituire un'eccezione per impostazione predefinita. fonte


2

puoi aggiungere l'URL jdbc con

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

Con l'aiuto di questo, sql converte "0000-00-00 00:00:00" come valore nullo.

per esempio:

jdbc:mysql:<host-name>/<db-name>?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.