JDBC'de DATETIME 0000-00-00 00:00:00 değerlerini işleme


85

Yapmaya çalışırsam bir istisna olur (aşağıya bakın)

resultset.getString("add_date");

0000-00-00 00:00:00 DATETIME değerini içeren bir MySQL veritabanına JDBC bağlantısı için (DATETIME için yarı boş değer), değeri bir dize olarak değil, yalnızca dize olarak almaya çalışsam da nesne.

Bunu yaparak aştım

SELECT CAST(add_date AS CHAR) as add_date

işe yarıyor, ama aptalca görünüyor ... bunu yapmanın daha iyi bir yolu var mı?

Demek istediğim, benim kendim ayrıştırmak böylece ben sadece ham DATETIME dizesini istiyor olmasıdır gibidir .

not: 0000'in geldiği yer: ( http://dev.mysql.com/doc/refman/5.0/en/datetime.html adresinden )

Geçersiz DATETIME, DATE veya TIMESTAMP değerleri, uygun türün ('0000-00-00 00:00:00' veya '0000-00-00') "sıfır" değerine dönüştürülür.

Özel istisna şudur:

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)

Yanıtlar:


85

Ben de aynı sorunu çözmeye çalışırken rastladım. Üzerinde çalıştığım kurulum JBOSS ve Hibernate kullanıyor, bu yüzden bunu farklı bir şekilde yapmam gerekiyordu. Temel durum için, zeroDateTimeBehavior=convertToNullbu yapılandırma özellikleri sayfasına göre bağlantı URI'nize ekleyebilmelisiniz .

Bu parametreyi hazırda bekletme yapılandırmanıza eklemeye atıfta bulunan arazide başka öneriler buldum:

In hibernate.cfg.xml :

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

In hibernate.properties :

hibernate.connection.zeroDateTimeBehavior=convertToNull

Ancak bunu JBOSS için mysql-ds.xml dosyama şu şekilde koymak zorunda kaldım :

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

Umarım bu birine yardımcı olur. :)


Hibernate.cfg.xml dosyasındaki değişikliği yapmak benim için hile yap, teşekkürler, çünkü Java hakkında neredeyse sıfır bilgim var
Gendrith

124

Alternatif yanıt, bu JDBC URL'sini doğrudan veri kaynağı yapılandırmanızda kullanabilirsiniz:

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

Düzenle:

Kaynak: MySQL Kılavuzu

Tamamen sıfır bileşenli veri zamanları (0000-00-00 ...) - Bu değerler Java'da güvenilir bir şekilde temsil edilemez. Bağlayıcı / J 3.0.x, bir ResultSet'ten okunurken bunları her zaman NULL'a dönüştürdü.

Connector / J 3.1, JDBC ve SQL standartlarına göre en doğru davranış olduğundan, bu değerlerle karşılaşıldığında varsayılan olarak bir istisna atar. Bu davranış, zeroDateTimeBehavior yapılandırma özelliği kullanılarak değiştirilebilir. İzin verilen değerler şunlardır:

  • S1009 SQLState ile bir SQLException oluşturan istisna (varsayılan).
  • convertToNull , tarih yerine NULL döndürür.
  • yuvarlak , tarihi en yakın değere, yani 0001-01-01'e yuvarlar.

Güncelleme: Alexander, bu özellik üzerinde mysql-connector-5.1.15'i etkileyen bir hata bildirdi. Resmi web sitesinde CHANGELOGS'a bakın .


ilginç. bu bilgiyi nerede buldun?
Jason S

cevabımı güncelledi! Ancak @sarumont'un URL'si bu durumda daha uygun olabilir (tüm bağlayıcı özelliklerini listeler).
Brian Clozel

1
jdbc yazılımının 5.1.16 sürümü şu hata düzeltmesini içeriyor: - BUG # 57808 için düzeltme - sıfırDateTimeBehavior, convertToNull olmasına rağmen getDate () içindeki 0000-00-00 değerine sahip DATE alanı için wasNull ayarlanmadı.
Alexander Kjäll

Vaov! Bilgi için teşekkürler. Sanırım bunu anlamakta zorlandınız :-(
Brian Clozel

11

Demek istediğim, sadece ham DATETIME dizesini istiyorum, böylece onu olduğu gibi ayrıştırabilirim.

Bu, "geçici çözümünüzün" geçici bir çözüm olmadığını, aslında veritabanındaki değeri kodunuza almanın tek yolu olduğunu düşündürüyor:

SELECT CAST(add_date AS CHAR) as add_date

Bu arada, MySQL belgelerinden birkaç not daha:

Geçersiz Verilerdeki MySQL Kısıtlamaları :

MySQL 5.0.2'den önce, MySQL yasadışı veya uygunsuz veri değerlerini affeder ve bunları veri girişi için yasal değerlere zorlar. MySQL 5.0.2 ve üzeri sürümlerde, bu varsayılan davranış olarak kalır, ancak sunucu SQL modunu değiştirerek kötü değerlerin daha geleneksel bir şekilde ele alınmasını seçebilirsiniz, böylece sunucu onları reddeder ve oluştukları ifadeyi iptal eder.

[..]

NULL'u NULL değerleri almayan bir sütuna kaydetmeye çalışırsanız, tek satırlı INSERT ifadeleri için bir hata oluşur. Çok satırlı INSERT ifadeleri için veya INSERT INTO ... SELECT ifadeleri için MySQL Sunucusu, sütun veri türü için örtük varsayılan değeri depolar.

MySQL 5.x Tarih ve Saat Türleri :

MySQL ayrıca '0000-00-00'ü "sahte tarih" olarak kaydetmenize de izin verir (NO_ZERO_DATE SQL modunu kullanmıyorsanız). Bu, bazı durumlarda NULL değerleri kullanmaktan daha uygundur (ve daha az veri ve dizin alanı kullanır).

[..]

Varsayılan olarak, MySQL aralık dışı olan veya tür için geçersiz olan bir tarih veya saat türü için bir değerle karşılaştığında (bu bölümün başında açıklandığı gibi), değeri o tür için "sıfır" değerine dönüştürür.


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

Hatayı önlemek için bunu kullanın. Tarihi dize biçiminde döndürür ve sonra onu bir dize olarak alabilirsiniz.

resultset.getString("dtime");

Bu aslında işe yaramıyor. GetString'i aramanıza rağmen. Dahili olarak mysql hala ilk olarak onu tarihe dönüştürmeye çalışır.

com.mysql.jdbc.ResultSetImpl.getDateFromString'de (ResultSetImpl.java:2270)

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

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

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


1
Bu, benim CAST (add_date AS CHAR) çözümümle aynıdır, ancak +1 çünkü bu, onu açıkça istediğiniz şekilde biçimlendirmenize izin verir.
Jason S

5

Satır ekledikten sonra:

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

hibernate.connection.zeroDateTimeBehavior=convertToNull

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

bir hata olmaya devam ediyor:

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

satırları bul:

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

sırasıyla aşağıdaki satırlarla değiştirin:

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

5

Bu sorunla mücadele ettim ve yukarıda tartışılan 'convertToNull' çözümlerini uyguladım. Yerel MySql örneğimde çalıştı. Ancak Play / Scala uygulamamı Heroku'ya dağıttığımda artık çalışmıyordu. Heroku ayrıca, kullanıcılara sağladıkları DB URL'sine birkaç bağımsız değişken ekler ve bu çözüm, Heroku'nun "?" kendi bağımsız değişkenlerinden önce çalışmayacaktır. Ancak, eşit derecede iyi çalışan farklı bir çözüm buldum.

SET sql_mode = 'NO_ZERO_DATE';

Bunu tablo açıklamalarıma koydum ve '0000-00-00 00:00:00' problemini çözdüm java.sql olarak temsil edilemiyor.


3

Boş bir değeri temsil etmek için null kullanmanızı öneririm.

Elde ettiğiniz istisna nedir?

BTW:

0 veya 0000 diye bir yıl yoktur. (Bu yıl bazı tarihler izin verse de)

Ve yılın 0 ayı veya ayın 0 günü yoktur. (Sorununuzun nedeni bu olabilir)


evet, ancak geriye dönük olarak bile 0 diye adlandırılan bir yıl yoktur. 1 AD / CE'den önceki yıl MÖ 1 / MÖ idi
Peter Lawrey

2
MySQL'in 0000'i geçersiz tarih olarak kullanmasının nedeni budur, çünkü mevcut tarihlerle çakışmaz. Ayrıca, 1999-00-00 gibi tarihler bazen belirli bir gün yerine 1999 yılını temsil etmek için kullanılır (benim tarafımdan değil!).
Jason S

3

'00 -00 -.... 'geçerli bir tarih olmadığını düşünerek sorunu çözdüm, ardından SQL sütun tanımımı boş değerlere izin vermek için "NULL" ifadesi ekleyerek değiştirdim:

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

Sonra, NULL değerleri ekleyebilmeliyim, bu "Bu zaman damgasını henüz kaydetmedim" anlamına gelir ...

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

Tablo şöyle görünüyor:

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)

Son olarak: JPA iş başında:

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

Sonunda bütün işi. Umarım bu sana yardımcı olur.


Bunun, tüm sıfırlardan oluşan bir MySQL DATETIME değerini geçerli bir tarihe dönüştürme sorununu ele aldığını sanmıyorum. Sadece bir DATETIME alanına null girileceğini gösterir ki bu da pek yardımcı olmayacaktır.
Mark Chorley

2

Diğer cevaplara eklemek için: Eğer 0000-00-00dizeyi istemiyorsanız, noDatetimeStringSync=true(saat dilimi dönüşümünü feda etme uyarısıyla) kullanabilirsiniz.

Resmi MySQL hatası: https://bugs.mysql.com/bug.php?id=47108 .

Ayrıca geçmiş için, JDBC tarihler NULLiçin 0000-00-00dönüyordu, ancak şimdi varsayılan olarak bir istisna döndürüyordu. Kaynak


2

jdbc url'sini ekleyebilirsin

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

Bunun yardımıyla sql '0000-00-00 00:00:00' null değerine dönüştürülür.

Örneğin:

jdbc:mysql:<host-name>/<db-name>?zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.