Aller au contenu | Aller au menu | Aller à la recherche


Java Date Time Timestamp avec une valeur de colonne NULL ou '0000-00-00 00:00:00'

Comment lire en java une date stockée dans un champs de votre table de votre base de donnée qui aurait une date incompatible avec les Dates java commençant en 1970 ?

La configuration de test, la table et les données

Connecteur JDBC

mysql-connector-java-5.0.8-bin.jar

Tables et données pour MySQL (5.0.32)

--
-- Structure de la table `test_date`
--
 
CREATE TABLE IF NOT EXISTS `test_date` (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `datetime_no_default_value` datetime NOT NULL,
  `datetime_default_null` datetime DEFAULT NULL,
  `timestamp_no_default_value` timestamp NOT NULL,
  `timestamp_default_null` timestamp NULL DEFAULT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM;
 
--
-- Contenu de la table `test_date`
--
 
INSERT INTO `test_date` 
(`id`, `datetime_no_default_value`, `datetime_default_null`, `timestamp_no_default_value`, `timestamp_default_null`) VALUES
(1, '0000-00-00 00:00:00', NULL, '0000-00-00 00:00:00', NULL);

Les erreurs

Avec datetime_no_default_value = '0000-00-00 00:00:00'

Exception in thread "main" java.sql.SQLException: Cannot convert value '0000-00-00 00:00:00' from column 2 to TIMESTAMP.

Erreur générée lors de la lecture de la date : '0000-00-00 00:00:00'

rs.getTimestamp("datetime_no_default_value");

Exception in thread "main" java.sql.SQLException: Value '0000-00-00 00:00:00' can not be represented as java.sql.Date

Erreur générée lors de la lecture de la date : '0000-00-00 00:00:00'

rs.getDate("datetime_no_default_value");

Exception in thread "main" java.sql.SQLException: java.sql.SQLException: Value '0000-00-00 00:00:00' can not be represented as java.sql.Time

Erreur générée lors de la lecture de la date : '0000-00-00 00:00:00'

rs.getTime("datetime_no_default_value");

Le NULL

Les 3 getters suivant retournent NULL, tel que l'indique la doc de java.sql.ResultSet : if the value is SQL NULL, the value returned is null in the Java programming language

rs.getDate("datetime_default_null");
rs.getTime("datetime_default_null");
rs.getTimestamp("datetime_default_null");

Aussi pour savoir si une cellule (enregistrement et colonne) a une valeur nulle, vous pouvez utiliser rs.wasNull() :

System.out.println(rs.getInt("id"));
if( rs.wasNull() )
	System.out.println("NULL");
else
	System.out.println("NOT NULL");
 
System.out.println(rs.getDate("datetime_default_null"));
if( rs.wasNull() )
	System.out.println("NULL");
else
	System.out.println("NOT NULL");			
 
System.out.println(rs.getBoolean("datetime_default_null"));

Selon le Cours JDBC 2008 :

Cas des valeurs NULL

  • Pour repérer les valeurs NULL de la base :
  • utiliser la méthode wasNull() de ResultSet
    • renvoie true si l’on vient de lire un NULL, false sinon
  • les méthodes getXXX() de ResultSet convertissent une valeur NULL SQL en une valeur acceptable par le type d’objet demandé :
    • les méthodes retournant un objet (getString(), getDate(),... ) retournent un NULL Java
    • les méthodes numériques (getByte() , getInt(),…) retournent "0"
    • getBoolean() retourne "false"

A partir de là il vous impossible de directement définir le setter sans obtenir :

Exception in thread "main" java.lang.NullPointerException
java.util.Date date = new java.util.Date();
date.setTime(rs.getTimestamp("datetime_default_null").getTime());
date.setTime(rs.getTime("datetime_default_null").getTime());
date.setTime(rs.getDate("datetime_default_null").getTime());
// Tous identique à :
date.setTime(null);

Pour lire cette date, vous serez donc obligé de récupérer l'exception par un bloc try catch

Paramétrage de la connexion MySQL pour la conversion des dates '0000-00-00 00:00:00' en NULL

Egalement, il vous arrivera peut être de lire une date ou datetime NOT NULL dont la valeur par default est '0000-00-00 00:00:00'. Le problème est que cela lève une exception lorsque vous lirez le champs avec un rs.getDate('champsDate'): java.sql.SQLException: Value '0000-00-00' can not be represented as java.sql.Date La documentation du Connector/J dans la section Upgrading from an Older Version indique :

Datetimes with all-zero components ("0000-00-00…") - These values can not be represented reliably in Java. Connector/J 3.0.x always converted them to NULL when being read from a ResultSet.

Connector/J 3.1 throws an exception by default when these values are encountered as this is the most correct behavior according to the JDBC and SQL standards. This behavior can be modified using the zeroDateTimeBehavior configuration property. The allowable values are:
exception (the default), which throws a SQLException with a SQLState of 'S1009',
convertToNull, which returns NULL instead of the date.
round, which rounds the date to the nearest closest value which is '0001-01-01'.

Starting with Connector/J 3.1.7, ResultSet.getString() can be decoupled from this behavior via noDatetimeStringSync=true (the default value is false) so that you can retrieve the unaltered all-zero value as a String. It should be noted that this also precludes using any time zone conversions, therefore the driver will not allow you to enable noDatetimeStringSync and useTimezone at the same time.

La solution est donc de typé a NULL son champs. Sinon en le laissant à NOT NULL il faut préciser le paramètre zeroDateTimeBehavior à convertToNull dans les propriétés de connexion du connecteur/J.

jdbc:mysql://localhost/database?user=root&password=secret&zeroDateTimeBehavior=convertToNull

Les classes Java pour la gestion des dates

Ressources

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

Fil des commentaires de ce billet