Problème de conversion Oracle SQL DATE avec iBATIS via Java JDBC

Je suis actuellement aux sockets avec un problème de conversion Oracle SQL DATE avec iBATIS à partir de Java.

J’utilise le pilote léger Oracle JDBC ojdbc14 version 10.2.0.4.0. iBATIS version 2.3.2. Java 1.6.0_10-rc2-b32.

Le problème concerne une colonne de type DATE renvoyée par cet extrait de code SQL:

SELECT * FROM TABLE(pk_invoice_qry.get_contract_rate(?,?,?,?,?,?,?,?,?,?)) order by from_date 

L’appel de procédure de package retourne un curseur de référence encapsulé dans une TABLE où il est alors facile de lire le jeu de résultats comme s’il s’agissait d’une requête de sélection sur une table.

Dans PL / SQL Developer, l’une des colonnes renvoyées, FROM_DATE, de type SQL DATE, a une précision par rapport à l’heure du jour:

 Tue Dec 16 23:59:00 PST 2008 

Mais lorsque j’y accède via iBATIS et JDBC, la valeur ne conserve que la précision au jour le jour:

 Tue Dec 16 12:00:00 AM PST 2008 

Ceci est plus clair lorsqu’il est affiché comme ceci:

Aurait du être:

 1229500740000 milliseconds since epoch Tuesday, December 16, 2008 11:59:00 PM PST 

Mais obtenir ceci à la place:

 1229414400000 milliseconds since epoch Tuesday, December 16, 2008 12:00:00 AM PST (as instance of class java.sql.Date) 

Quoi que j’essaie, je suis incapable d’exposer toute la précision de cette colonne DATE à renvoyer via Java JDBC et iBATIS.

Voici ce que mappe iBATIS:

 FROM_DATE : 2008-12-03 : class java.sql.Date 

Le mappage actuel d’iBATIS est le suivant:

  

J’ai aussi essayé:

  

ou

  

Mais toutes les tentatives de mappage produisent la même valeur Date tronquée. C’est comme si JDBC avait déjà subi le dommage de perdre la précision des données avant même que iBATIS ne le touche.

Il est clair que je perds une partie de ma précision des données en passant par JDBC et iBATIS, ce qui n’est pas le cas lorsque je rest dans PL / SQL Developer et que le même fragment SQL est utilisé comme script de test. Pas acceptable du tout, très frustrant et finalement très effrayant.

Les informations complètes (et elles sont plus complexes que celles décrites ici et peuvent dépendre de la version des pilotes Oracle utilisée) figurent dans la réponse de Richard Yee – [lien maintenant expiré à Nabble]


Rapide saisir avant qu’il expire de nabble …

Roger, voir: http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-faq-090281.html#08_01

Plus précisément: Types de données simples Que se passe-t-il avec DATE et TIMESTAMP? Cette section concerne les types de données simples. 🙂

Avant la version 9.2, les pilotes JDBC Oracle mappaient le type DATE SQL sur java.sql.Timestamp. Cela a eu un certain sens car le type Oracle DATE SQL contient les informations de date et d’heure, tout comme java.sql.Timestamp. La correspondance la plus évidente avec java.sql.Date était quelque peu problématique car java.sql.Date n’incluait pas les informations de temps. Il est également arrivé que le SGBDR ne prenne pas en charge le type TIMESTAMP SQL. Par conséquent, le mappage de DATE sur Timestamp ne posait aucun problème.

En 9.2, le support TIMESTAMP a été ajouté au SGBDR. La différence entre DATE et TIMESTAMP est que TIMESTAMP inclut des nanosecondes et que DATE ne l’a pas. Ainsi, à partir de la version 9.2, DATE est mappé sur Date et TIMESTAMP est mappé sur Timestamp. Malheureusement, si vous vous basez sur les valeurs DATE pour contenir des informations temporelles, il y a un problème.

Il y a plusieurs façons de résoudre ce problème:

Modifiez vos tables pour utiliser TIMESTAMP au lieu de DATE. C’est probablement rarement possible, mais c’est la meilleure solution à ce moment-là.

Modifiez votre application pour utiliser defineColumnType afin de définir les colonnes sous la forme TIMESTAMP plutôt que DATE. Cela pose des problèmes car vous ne voulez vraiment pas utiliser defineColumnType à moins d’être obligé de le faire (voir Qu’est-ce que defineColumnType et quand dois-je l’utiliser?).

Modifiez votre application pour utiliser getTimestamp plutôt que getObject. C’est une bonne solution lorsque cela est possible. Cependant, de nombreuses applications contiennent du code générique reposant sur getObject. Ce n’est donc pas toujours possible.

Définissez la propriété de connexion V8Compatible. Cela indique aux pilotes JDBC d’utiliser l’ancien mappage plutôt que le nouveau. Vous pouvez définir cet indicateur en tant que propriété de connexion ou propriété système. Vous définissez la propriété de connexion en l’ajoutant à l’object java.util.Properties transmis à DriverManager.getConnection ou à OracleDataSource.setConnectionProperties. Vous définissez la propriété système en incluant une option -D dans votre ligne de commande java.

java -Doracle.jdbc.V8Compatible = “true” MonApp Oracle JDBC 11.1 résout ce problème. À partir de cette version, le pilote mappe les colonnes SQL DATE à java.sql.Timestamp par défaut. Il n’est pas nécessaire de définir V8Compatible pour obtenir le mappage correct. V8Compatible est fortement déconseillé. Vous ne devriez pas l’utiliser du tout. Si vous le définissez sur true, cela ne fera aucun mal, mais vous devriez arrêter de l’utiliser.

Bien que cela ait rarement été utilisé de cette façon, V8Compatible existait non pour résoudre le problème de DATE to Date, mais pour prendre en charge la compatibilité avec les bases de données 8i. Les bases de données 8i (et antérieures) ne prenaient pas en charge le type TIMESTAMP. La définition de V8Compatible a non seulement entraîné le mappage de SQL DATE sur l’horodatage lors de la lecture de la firebase database, mais également la conversion de tous les horodatages en SQL DATE lors de l’écriture dans la firebase database. Étant donné que 8i est désactivé, les pilotes JDBC 11.1 ne prennent pas en charge ce mode de compatibilité. Pour cette raison, V8Compatible est désactivé.

Comme indiqué ci-dessus, les pilotes 11.1 convertissent par défaut SQL DATE en horodatage lors de la lecture de la firebase database. C’était toujours la bonne chose à faire et le changement de 9i était une erreur. Les pilotes 11.1 sont revenus au bon comportement. Même si vous n’avez pas défini V8Compatible dans votre application, vous ne devriez voir aucune différence de comportement dans la plupart des cas. Vous remarquerez peut-être une différence si vous utilisez getObject pour lire une colonne DATE. Le résultat sera un horodatage plutôt qu’une date. Puisque l’horodatage est une sous-classe de Date, cela ne pose généralement pas de problème. Vous remarquerez peut-être une différence si vous utilisez la conversion de DATE en Date pour tronquer le composant heure ou si vous faites toSsortingng sur la valeur. Sinon, le changement devrait être transparent.

Si, pour une raison quelconque, votre application est très sensible à ce changement et que vous devez simplement avoir le comportement 9i-10g, vous pouvez définir une propriété de connexion. Définissez mapDateToTimestamp sur false et le pilote reviendra au comportement par défaut 9i-10g et mappera DATE à Date.

Si possible, changez votre type de colonne en TIMESTAMP au lieu de DATE.

-Richard


Roger Voss a écrit: J’ai posté la question suivante / problème sur stackoverflow, donc si quelqu’un connaît une résolution, il serait bon de voir qu’il a répondu ici:

Problème de conversion Oracle SQL DATE avec iBATIS via Java JDBC

Voici la description du problème:

Je suis actuellement aux sockets avec un problème de conversion Oracle SQL DATE utilisant iBATIS à partir de Java.

J’utilise le pilote léger Oracle JDBC ojdbc14 version 10.2.0.4.0. iBATIS version 2.3.2. Java 1.6.0_10-rc2-b32.

Le problème concerne une colonne de type DATE renvoyée par cet extrait de code SQL:

SELECT * FROM TABLE (pk_invoice_qry.get_contract_rate (?,?,?,?,?,?,?,?,?,?,?,?)) Commander par from_date

L’appel de procédure de package retourne un curseur de référence encapsulé dans une TABLE où il est alors facile de lire le jeu de résultats comme s’il s’agissait d’une requête de sélection sur une table.

Dans PL / SQL Developer, l’une des colonnes renvoyées, FROM_DATE, de type SQL DATE, a une précision par rapport à l’heure du jour:

 Tue Dec 16 23:59:00 PST 2008 

Mais lorsque j’y accède via iBATIS et JDBC, la valeur ne conserve que la précision au jour le jour:

 Tue Dec 16 12:00:00 AM PST 2008 

Ceci est plus clair lorsqu’il est affiché comme ceci:

Aurait dû être: 1229500740000 millisecondes depuis l’époque mardi, 16 décembre 2008 23:59:00 HNP

Mais obtenir ceci à la place: 1229414400000 millisecondes depuis l’époque mardi, 16 décembre 2008 12:00 HST (comme instance de la classe java.sql.Date)

Quoi que j’essaie, je suis incapable d’exposer toute la précision de cette colonne DATE à renvoyer via Java JDBC et iBATIS.

Voici ce que mappe iBATIS:

FROM_DATE: 2008-12-03: classe java.sql.Date

Le mappage actuel d’iBATIS est le suivant:

J’ai aussi essayé:

ou

Mais toutes les tentatives de mappage produisent la même valeur Date tronquée. C’est comme si JDBC avait déjà fait le mal de perdre la précision des données avant même qu’iBATIS ne le touche.

Clairement, je perds une partie de ma précision de données en passant par JDBC et iBATIS, ce qui n’est pas le cas lorsque je rest dans PL / SQL Developer pour exécuter le même fragment SQL que le script de test. Pas acceptable du tout, très frustrant et finalement très effrayant.

J’ai découvert comment résoudre ce problème. iBATIS permet aux gestionnaires de types personnalisés d’être enregistrés. Donc, dans mon fichier sqlmap-config.xml, j’ai ajouté ceci:

   

Et puis ajouté cette classe qui implémente l’interface iBATIS TypeHandlerCallback:

 // corrected getResult()/setParameter() to correctly deal with when value is null public class CustomDateHandler implements TypeHandlerCallback { @Override public Object getResult(ResultGetter getter) throws SQLException { final Object obj = getter.getTimestamp(); return obj != null ? (Date) obj : null; } @Override public void setParameter(ParameterSetter setter,Object value) throws SQLException { setter.setTimestamp(value != null ? new Timestamp(((Date)value).getTime()) : null); } @Override public Object valueOf(Ssortingng datetime) { return Timestamp.valueOf(datetime); } } 

Chaque fois que je dois mapper un Oracle DATE, je le décris comme suit:

  

J’ai résolu mon problème en utilisant jdbcType = “TIMESTAMP” au lieu de jdbcType = “DATE”

• PROBLÈME:

• RESOLU:

Cordialement.

Pedro

Le problème concerne le pilote Oracle.

La meilleure solution que j’ai trouvée consistait à changer tout jdbcType = “DATE” en jdbcType = “TIMESTAMP” et tout #nom_colonne: DATE # en #nom_colonne: TIMESTAMP #

Alors changez:

  

à

  

Le problème est l’utilisation de java.sql.Date . Selon la Javadoc , les valeurs en millisecondes java.sql.Date par une instance java.sql.Date doivent être «normalisées» en définissant les heures, les minutes, les secondes et les millisecondes sur zéro dans le fuseau horaire particulier auquel l’instance est associée, pour se conformer à la définition de SQL DATE .

Oui, je vois. Le standard SQL DATE doit consister uniquement à stocker la résolution au jour le jour. En effet, voici un extrait du type DATE d’Oracle:

Oracle prend en charge la date et l’heure, bien que différemment du standard SQL2. Plutôt que d’utiliser deux entités distinctes, la date et l’heure, Oracle en utilise une seule, la DATE. Le type DATE est stocké dans un format interne spécial qui comprend non seulement le mois, le jour et l’année, mais également l’heure, les minutes et les secondes.

Ce qui indique que la date d’Oracle dépasse la date standard SQL.

Hmm, les utilisateurs Oracle PL / SQL utilisent largement DATE pour conserver les valeurs dont ils dépendent lorsque la résolution est à la seconde près. On dirait qu’iBATIS a besoin de quelque chose comme le concept de dialecte Hibernate SQL où, au lieu d’interpréter DATE via java.sql.Date, pourrait remplacer et interpréter via java.util.Date, que Javadocs définit comme permettant une résolution en millisecondes.

Malheureusement, lorsque j’ai changé le mappage en quelque chose comme:

  

ou

  

Il semble toujours que la première date de SQL ait été traduite en java.sql.Date, ce qui a entraîné une perte de précision de l’heure.

Richard Yee mentionne que les derniers pilotes Oracle résolvent le problème. Je peux le confirmer. Avait le même problème ici avec les pilotes 10.2, mis à niveau aujourd’hui à ojdbc5.jar (11.2.0.1.0), et le problème est parti maintenant.