Oracle
 sql >> Base de données >  >> RDS >> Oracle

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

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

Prise rapide avant qu'il n'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 simplesQue se passe-t-il avec DATE et TIMESTAMP ?Cette section concerne les types de données simples. :-)

Avant la version 9.2, les pilotes Oracle JDBC mappaient le type DATE SQL sur java.sql.Timestamp. Cela avait un certain sens car le type Oracle DATE SQL contient à la fois des informations de date et d'heure, tout comme java.sql.Timestamp. Le mappage le plus évident vers java.sql.Date était quelque peu problématique car java.sql.Date n'inclut pas d'informations temporelles. Il arrivait également que le RDBMS ne prenne pas en charge le type SQL TIMESTAMP, il n'y avait donc aucun problème avec le mappage de DATE à Timestamp.

Dans la version 9.2, la prise en charge de TIMESTAMP a été ajoutée au RDBMS. La différence entre DATE et TIMESTAMP est que TIMESTAMP inclut les nanosecondes et pas DATE. Ainsi, à partir de la version 9.2, DATE est mappé sur Date et TIMESTAMP est mappé sur Timestamp. Malheureusement, si vous comptiez sur les valeurs DATE pour contenir les informations temporelles, il y a un problème.

Il existe 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 quand c'est le cas.

Modifiez votre application pour utiliser defineColumnType pour définir les colonnes en tant que TIMESTAMP plutôt que DATE. Cela pose des problèmes car vous ne voulez vraiment pas utiliser defineColumnType à moins que vous n'y soyez obligé (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, mais de nombreuses applications contiennent du code générique qui repose 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'objet 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" MyAppOracle JDBC 11.1 corrige ce problème. À partir de cette version, le pilote mappe les colonnes SQL DATE sur 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éprécié. 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 qu'il ait été rarement utilisé de cette façon, V8Compatible n'existait pas pour résoudre le problème 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 à partir de la base de données, mais également la conversion de tous les horodatages en SQL DATE lors de leur écriture dans la base de données. Étant donné que 8i n'est plus pris en charge, les pilotes JDBC 11.1 ne prennent pas en charge ce mode de compatibilité. Pour cette raison, V8Compatible n'est plus pris en charge.

Comme mentionné ci-dessus, les pilotes 11.1 convertissent par défaut SQL DATE en Timestamp lors de la lecture à partir de la base de données. C'était toujours la bonne chose à faire et le changement de 9i était une erreur. Les pilotes 11.1 ont retrouvé le comportement correct. 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. Étant donné que Timestamp est une sous-classe de Date, ce n'est généralement pas un problème. Là où vous remarquerez peut-être une différence, c'est si vous vous êtes appuyé sur la conversion de DATE en Date pour tronquer le composant de temps ou si vous faites toString 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, vous devez modifier votre type de colonne en TIMESTAMP au lieu de DATE.

-Richard

Roger Voss a écrit :J'ai posté la question/le problème suivant sur stackoverflow, donc si quelqu'un connaît une solution, ce serait bien d'y voir une réponse :

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

Voici la description du problème :

Je suis actuellement aux prises avec un problème de conversion Oracle sql DATE en utilisant iBATIS 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 tourne autour d'une colonne de type DATE renvoyée par cet extrait de SQL :

SELECT *FROM TABLE(pk_invoice_qry.get_contract_rate(?,?,?,?,?,?,?,?,?,?)) commander avant from_date

L'appel de procédure de package renvoie un curseur de référence qui est enveloppé dans un 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 sur 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 la précision qu'à ce jour :

Tue Dec 16 12:00:00 AM PST 2008

C'est plus clair lorsqu'il est affiché comme ceci :

Aurait dû être :1229500740000 millisecondes depuis epochMardi 16 décembre 2008 23:59:00 PST

Mais obtenir ceci à la place :1229414400000 millisecondes depuis epochMardi 16 décembre 2008 00:00:00 AM PST(as instance of class java.sql.Date)

Peu importe ce que j'essaie, je ne parviens pas à exposer la précision complète de cette colonne DATE à renvoyer via Java JDBC et iBATIS.

Ce qu'iBATIS est en train de cartographier est ceci :

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

Le mappage iBATIS actuel est le suivant :

J'ai aussi essayé :

ou

Mais tous les mappages tentés donnent la même valeur Date tronquée. C'est comme si JDBC avait déjà fait le mal en perdant la précision des données avant même qu'iBATIS n'y touche.

Il est clair que je perds une partie de la précision de mes données en passant par JDBC et iBATIS, ce qui ne se produit pas lorsque je reste dans PL/SQL Developer exécutant le même extrait SQL qu'un script de test. Pas acceptable du tout, très frustrant et finalement très effrayant.