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)

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


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 .


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.


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]


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"));

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.


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)


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.


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


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.