obsługa wartości DATETIME 0000-00-00 00:00:00 w JDBC

85

Jeśli spróbuję, dostaję wyjątek (patrz poniżej)

resultset.getString("add_date");

dla połączenia JDBC z bazą danych MySQL zawierającą DATETIME wartość 0000-00-00 00:00:00 (quasi-zerowa wartość dla DATETIME), mimo że próbuję tylko uzyskać wartość jako ciąg, a nie jako obiekt.

Poradziłem sobie z tym, robiąc

SELECT CAST(add_date AS CHAR) as add_date

co działa, ale wydaje się głupie ... czy istnieje lepszy sposób na zrobienie tego?

Chodzi mi o to, że po prostu chcą surowego DATETIME ciąg, więc mogę sobie zanalizować to jak jest .

uwaga: tutaj pojawia się 0000: (z http://dev.mysql.com/doc/refman/5.0/en/datetime.html )

Niedozwolone wartości DATETIME, DATE lub TIMESTAMP są konwertowane na wartość „zero” odpowiedniego typu („0000-00-00 00:00:00” lub „0000-00-00”).

Szczególnym wyjątkiem jest ten:

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.
źródło

Odpowiedzi:

85

Natknąłem się na to, próbując rozwiązać ten sam problem. Instalacja, z którą pracuję, wykorzystuje JBOSS i Hibernate, więc musiałem zrobić to inaczej. W przypadku podstawowym powinno być możliwe dodanie zeroDateTimeBehavior=convertToNullidentyfikatora URI połączenia zgodnie z tą stroną właściwości konfiguracyjnych .

Znalazłem inne sugestie w całym kraju dotyczące umieszczenia tego parametru w konfiguracji hibernacji:

W pliku hibernate.cfg.xml :

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

W hibernate.properties :

hibernate.connection.zeroDateTimeBehavior=convertToNull

Ale musiałem umieścić go w moim pliku mysql-ds.xml dla JBOSS jako:

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

Mam nadzieję, że to komuś pomoże. :)

sarumont
źródło
Zmiana w pliku hibernate.cfg.xml
załatwia sprawę
124

Alternatywna odpowiedź: możesz użyć tego adresu URL JDBC bezpośrednio w konfiguracji źródła danych:

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

Edytować:

Źródło: Podręcznik MySQL

Czasy dat ze składnikami zerowymi (0000-00-00 ...) - Te wartości nie mogą być wiarygodnie reprezentowane w Javie. Connector / J 3.0.x zawsze konwertował je na NULL podczas odczytu z zestawu wyników.

Connector / J 3.1 domyślnie zgłasza wyjątek w przypadku napotkania tych wartości, ponieważ jest to najbardziej poprawne zachowanie zgodnie ze standardami JDBC i SQL. To zachowanie można zmodyfikować za pomocą właściwości konfiguracji zeroDateTimeBehavior. Dopuszczalne wartości to:

  • wyjątek (wartość domyślna), który zgłasza SQLException z SQLState S1009.
  • convertToNull , która zwraca NULL zamiast daty.
  • round , która zaokrągla datę do najbliższej najbliższej wartości, czyli 0001-01-01.

Aktualizacja: Alexander zgłosił błąd dotyczący mysql-connector-5.1.15 w tej funkcji. Zobacz CHANGELOGS na oficjalnej stronie internetowej .

Brian Clozel
źródło
ciekawy. gdzie znalazłeś te informacje?
Jason S
właśnie zaktualizowałem moją odpowiedź! Ale adres URL @sarumont może lepiej pasować w tym przypadku (zawiera listę wszystkich właściwości łącznika).
Brian Clozel
1
Wersja 5.1.16 oprogramowania jdbc zawiera tę poprawkę: - Poprawka błędu nr 57808 - nie ustawiono parametru wasNull dla pola DATE o wartości 0000-00-00 w getDate (), chociaż wartość zeroDateTimeBehavior jest convertToNull.
Alexander Kjäll
Łał! Dzięki za informację. Myślę, że trudno ci było to
rozgryźć
Adres URL ... Życie. Ocaleni!
CodingInCircles
11

Chodzi mi o to, że chcę tylko nieprzetworzonego ciągu DATETIME, więc mogę go przeanalizować samodzielnie.

To sprawia, że ​​myślę, że twoje „obejście” nie jest obejściem, ale w rzeczywistości jedynym sposobem na pobranie wartości z bazy danych do kodu:

SELECT CAST(add_date AS CHAR) as add_date

Przy okazji, kilka uwag z dokumentacji MySQL:

Ograniczenia MySQL dotyczące nieprawidłowych danych :

Przed MySQL 5.0.2 MySQL wybacza nielegalne lub niewłaściwe wartości danych i wymusza na nich legalne wartości przy wprowadzaniu danych. W MySQL 5.0.2 i nowszych jest to zachowanie domyślne, ale można zmienić tryb SQL serwera, aby wybrać bardziej tradycyjne traktowanie złych wartości, tak że serwer je odrzuca i przerywa instrukcję, w której występują.

[..]

Jeśli spróbujesz zapisać NULL w kolumnie, która nie przyjmuje wartości NULL, wystąpi błąd dla jednorzędowych instrukcji INSERT. W przypadku instrukcji INSERT z wieloma wierszami lub instrukcji INSERT INTO ... SELECT serwer MySQL przechowuje niejawną wartość domyślną dla typu danych kolumny.

Typy daty i godziny MySQL 5.x :

MySQL pozwala także na przechowywanie '0000-00-00' jako „fikcyjnej daty” (jeśli nie używasz trybu SQL NO_ZERO_DATE). W niektórych przypadkach jest to wygodniejsze (i zużywa mniej danych i miejsca na indeksy) niż używanie wartości NULL.

[..]

Domyślnie, gdy MySQL napotka wartość typu daty lub godziny, która jest poza zakresem lub w inny sposób jest niezgodna z typem (jak opisano na początku tej sekcji), konwertuje wartość na wartość „zero” dla tego typu.

Arjan
źródło
6
DATE_FORMAT(column name, '%Y-%m-%d %T') as dtime

Użyj tego, aby uniknąć błędu. Zwraca datę w formacie ciągu, a następnie można ją pobrać jako ciąg.

resultset.getString("dtime");

To faktycznie NIE działa. Nawet jeśli wywołujesz metodę getString. Wewnętrznie mysql nadal najpierw próbuje przekonwertować go na datę.

pod adresem 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] at com.mysql.jdbc.ResultSetImpl.getString (ResultSetImpl.java:5576)

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

Społeczność
źródło
1
To trochę to samo, co moje rozwiązanie CAST (add_date AS CHAR), ale +1, ponieważ pozwala to sformatować je jawnie tak, jak chcesz.
Jason S
5

Jeśli po dodaniu linii:

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

hibernate.connection.zeroDateTimeBehavior=convertToNull

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

nadal jest błędem:

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

znajdź linie:

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

zastąpić odpowiednio następującymi wierszami:

1) Time.valueOf(resultSet.getString("time"));
2) Timestamp.valueOf(resultSet.getString("timestamp"));
3) Date.valueOf(resultSet.getString("date"));
Vadim
źródło
5

Zmagałem się z tym problemem i zaimplementowałem omówione powyżej rozwiązania „convertToNull”. Zadziałało w mojej lokalnej instancji MySql. Ale kiedy wdrożyłem aplikację Play / Scala na Heroku, przestała działać. Heroku łączy również kilka argumentów z adresem URL bazy danych, które dostarczają użytkownikom, i to rozwiązanie, ponieważ Heroku używa konkatenacji „?” przed ich własnym zestawem argumentów, nie zadziała. Jednak znalazłem inne rozwiązanie, które wydaje się działać równie dobrze.

SET sql_mode = 'NO_ZERO_DATE';

Umieściłem to w moich opisach tabel i rozwiązało to problem „0000-00-00 00:00:00”, którego nie można przedstawić jako java.sql.

Aqume
źródło
3

Sugeruję użycie wartości null do reprezentowania wartości zerowej.

Jaki wyjątek otrzymujesz?

BTW:

Nie ma roku o nazwie 0 lub 0000 (chociaż niektóre daty pozwalają na ten rok)

I nie ma 0 miesiąca w roku ani 0 dnia miesiąca. (Co może być przyczyną twojego problemu)

Peter Lawrey
źródło
tak, ale nadal nie ma roku nazwanego 0, nawet retrospektywnie. Rok przed 1 AD / CE był 1 BC / BC
Peter Lawrey
2
Z tego powodu MySQL używa 0000 jako nieprawidłowej daty, ponieważ nie powoduje konfliktu z istniejącymi datami. Ponadto daty takie jak 1999-00-00 są czasami używane (nie przeze mnie!) Do reprezentowania roku 1999, a nie konkretnego dnia.
Jason S
3

Rozwiązałem problem, biorąc pod uwagę, że „00 -00 -....” nie jest prawidłową datą, a następnie zmieniłem definicję kolumny SQL, dodając wyrażenie „NULL”, aby zezwolić na wartości 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),...

Następnie mogę wstawić wartości NULL, co oznacza "Nie zarejestrowałem jeszcze tego znacznika czasu" ...

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

Tabela wygląda tak:

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)

Wreszcie: JPA w akcji:

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

Nareszcie cała jego praca. Mam nadzieję, że ci to pomoże.

EdU
źródło
Nie sądzę, aby to rozwiązało problem konwersji MySQL DATETIME wszystkich zer na prawidłową datę. Pokazuje tylko, jak wprowadzić wartość null w pole DATETIME, co tak naprawdę niewiele pomoże.
Mark Chorley
2

Aby dodać do innych odpowiedzi: Jeśli nie chcesz 0000-00-00ciągu, możesz użyć noDatetimeStringSync=true(z zastrzeżeniem poświęcenia konwersji strefy czasowej).

Oficjalny błąd MySQL: https://bugs.mysql.com/bug.php?id=47108 .

Ponadto, dla historii, JDBC używany do powrotu NULLdo 0000-00-00dat, ale teraz powrócić wyjątku domyślnie. Źródło

damio
źródło
2

możesz dołączyć adres URL jdbc za pomocą

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

Z pomocą tego sql konwertuje „0000-00-00 00:00:00” na wartość null.

na przykład:

jdbc:mysql:<host-name>/<db-name>?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8
Mukul Aggarwal
źródło