web-dev-qa-db-fra.com

traitement des valeurs DATETIME 0000-00-00 00:00:00 dans JDBC

Je reçois une exception (voir ci-dessous) si j'essaie de le faire

resultset.getString("add_date");

pour une connexion JDBC à une base de données MySQL contenant une valeur DATETIME de 0000-00-00 00:00:00 (la valeur quasi nulle pour DATETIME), même si j'essaie simplement d'obtenir la valeur sous forme de chaîne, pas sous forme de objet.

J'ai contourné ça en faisant

SELECT CAST(add_date AS CHAR) as add_date

qui fonctionne, mais semble stupide ... y at-il une meilleure façon de faire cela?

Mon point est que je veux juste la chaîne brute DATETIME, afin que je puisse l’analyser moi-même telle quelle .

remarque: voici où le code 0000 entre: (de http://dev.mysql.com/doc/refman/5.0/en /datetime.html )

Les valeurs DATETIME, DATE ou TIMESTAMP illégales sont converties en la valeur "zéro" du type approprié ("0000-00-00 00:00:00" ou "0000-00-00").

L'exception spécifique est celle-ci:

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)
83
Jason S

Je suis tombé sur cette tentative de résoudre le même problème. L'installation sur laquelle je travaille utilise JBOSS et Hibernate, j'ai donc dû le faire différemment. Pour le cas de base, vous devriez pouvoir ajouter zeroDateTimeBehavior=convertToNull à votre adresse URI de connexion conformément à ceci page des propriétés de configuration .

J'ai trouvé d'autres suggestions à travers le pays faisant référence à l'insertion de ce paramètre dans votre configuration d'hibernation:

Dans hibernate.cfg.xml :

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

Dans hibernate.properties :

hibernate.connection.zeroDateTimeBehavior=convertToNull

Mais je devais le mettre dans mon fichier mysql-ds.xml pour JBOSS en tant que:

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

J'espère que ça aide quelqu'un. :)

81
sarumont

Réponse alternative, vous pouvez utiliser cette URL JDBC directement dans la configuration de votre source de données:

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

Modifier:

Source: Manuel MySQL

Les dates avec des composants nuls (0000-00-00 ...) - Ces valeurs ne peuvent pas être représentées de manière fiable en Java. Connector/J 3.0.x les convertissait toujours en NULL lors de la lecture à partir d'un ResultSet.

Connector/J 3.1 lève une exception par défaut lorsque ces valeurs sont rencontrées car il s'agit du comportement le plus correct selon les normes JDBC et SQL. Ce comportement peut être modifié à l'aide de la propriété de configuration zeroDateTimeBehavior. Les valeurs autorisées sont:

  • exception (valeur par défaut), qui lève une exception SQLException avec un état SQLState de S1009.
  • convertToNull , qui renvoie NULL au lieu de la date.
  • round , qui arrondit la date à la valeur la plus proche, 0001-01-01.

Mise à jour: Alexander a signalé un bogue affectant mysql-connector-5.1.15 sur cette fonctionnalité. Voir CHANGELOGS sur le site officiel .

122
Brian Clozel

Ce que je veux dire, c'est que je veux juste la chaîne brute DATETIME, afin que je puisse l'analyser moi-même telle quelle.

Cela me fait penser que votre "solution de contournement" n'est pas une solution de contournement, mais constitue en fait le seul moyen d'obtenir la valeur de la base de données dans votre code:

SELECT CAST(add_date AS CHAR) as add_date

À propos, quelques notes supplémentaires tirées de la documentation MySQL:

MySQL Contraintes sur les données invalides :

Avant MySQL 5.0.2, MySQL pardonnait les valeurs de données illégales ou inappropriées et les contraignait à des valeurs légales pour la saisie de données. Dans MySQL 5.0.2 et versions ultérieures, cela reste le comportement par défaut, mais vous pouvez modifier le mode SQL du serveur pour sélectionner un traitement plus traditionnel des valeurs incorrectes, de sorte que le serveur les rejette et abandonne l'instruction dans laquelle elles se produisent.

[..]

Si vous essayez de stocker NULL dans une colonne qui ne prend pas de valeurs NULL, une erreur se produit pour les instructions INSERT à une seule ligne. Pour les instructions INSERT à plusieurs lignes ou pour les instructions INSERT INTO ... SELECT, le serveur MySQL stocke la valeur implicite par défaut du type de données de la colonne.

MySQL 5.x types date et heure :

MySQL vous permet également de stocker "0000-00-00" comme "date fictive" (si vous n'utilisez pas le mode SQL NO_ZERO_DATE). Ceci est dans certains cas plus pratique (et utilise moins de données et d'espace d'index) que d'utiliser des valeurs NULL.

[..]

Par défaut, lorsque MySQL rencontre une valeur pour un type de date ou d’heure qui est en dehors des limites ou illégale pour le type (comme décrit au début de cette section), il convertit la valeur en valeur "zéro" pour ce type.

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

Utilisez ceci pour éviter l'erreur. Il retourne la date au format chaîne et vous pouvez ensuite l'obtenir sous forme de chaîne.

resultset.getString("dtime");

Cela ne fonctionne pas réellement. Même si vous appelez getString. En interne, mysql essaie toujours de le convertir en premier.

à l'adresse com.mysql.jdbc.ResultSetImpl.getDateFromString (ResultSetImpl.Java:2270)

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

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

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

6
Atul

Si, après avoir ajouté des lignes:

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

hibernate.connection.zeroDateTimeBehavior=convertToNull

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

continue à être une erreur:

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

trouver des lignes:

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

remplacez par les lignes suivantes, respectivement:

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

J'ai lutté avec ce problème et mis en œuvre les solutions 'convertToNull' discutées ci-dessus. Cela a fonctionné dans mon instance locale MySql. Mais lorsque j'ai déployé mon application Play/Scala sur Heroku, cela ne fonctionnerait plus. Heroku concatène également plusieurs arguments à l'URL de la base de données qu'ils fournissent aux utilisateurs, et à cette solution, en raison de la concaténation par Heroku de "?" avant leurs propres arguments, ne fonctionnera pas. Cependant, j'ai trouvé une solution différente qui semble fonctionner aussi bien.

SET sql_mode = 'NO_ZERO_DATE';

Je mets cela dans la description de mon tableau et cela résout le problème de '0000-00-00 00:00:00' ne peut pas être représenté par Java.sql.Timestamp

5
Aqume

Je vous suggère d'utiliser null pour représenter une valeur null.

Quelle est l'exception que vous obtenez?

BTW:

Il n'y a pas d'année appelée 0 ou 0000. (Bien que certaines dates le permettent cette année)

Et il n'y a pas 0 mois de l'année ou 0 jour du mois. (Ce qui peut être la cause de votre problème)

3
Peter Lawrey

J'ai résolu le problème en considérant que '00 -00 -.... 'n'était pas une date valide, puis j'ai modifié la définition de la colonne SQL en ajoutant l'expression "NULL" pour autoriser les valeurs 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),...

Ensuite, je dois pouvoir insérer des valeurs NULL, ce qui signifie "Je n'ai pas encore enregistré cet horodatage" ...

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

La table regarde ça:

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)

Enfin: JPA en action:

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

Enfin tout son travail. J'espère que cela vous aidera.

3
EdU

Pour ajouter aux autres réponses: Si vous voulez le 0000-00-00 chaîne, vous pouvez utiliser noDatetimeStringSync=true (avec l’avertissement de sacrifier la conversion de fuseau horaire).

Le bogue officiel de MySQL: https://bugs.mysql.com/bug.php?id=47108 .

De plus, pour l’historique, JDBC renvoyait NULL pour 0000-00-00 dates mais renvoie maintenant une exception par défaut. Source

2
damio

vous pouvez ajouter l'URL jdbc avec

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

Avec l'aide de ceci, sql convertit '0000-00-00 00:00:00' comme valeur nulle.

par exemple:

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