type small number long data java oracle date jdbc ibatis

small - Problema de conversión de Oracle SQL DATE usando iBATIS a través de Java JDBC



short java (7)

El problema es el uso de java.sql.Date . Según el Javadoc , los valores de milisegundos envueltos por una instancia de java.sql.Date deben ''normalizarse'' al establecer las horas, minutos, segundos y milisegundos en cero en la zona horaria particular a la que está asociada la instancia, para cumplir con la definición de SQL DATE .

Actualmente estoy luchando con un problema de conversión de Oracle SQL DATE usando iBATIS de Java.

Estoy usando el controlador delgado Oracle JDBC ojdbc14 versión 10.2.0.4.0. iBATIS versión 2.3.2. Java 1.6.0_10-rc2-b32.

El problema gira en torno a una columna de tipo DATE que devuelve este fragmento de SQL:

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

La llamada al procedimiento de paquete devuelve un cursor de ref que se ajusta en una TABLA donde es fácil leer el conjunto de resultados como si fuera una consulta de selección en una tabla.

En PL / SQL Developer, una de las columnas devueltas, FROM_DATE, del tipo SQL DATE, tiene precisión a la hora del día:

Tue Dec 16 23:59:00 PST 2008

Pero cuando accedo a esto a través de iBATIS y JDBC, el valor solo conserva la precisión del día:

Tue Dec 16 12:00:00 AM PST 2008

Esto es más claro cuando se muestra así:

Debería haber sido:

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

Pero obteniendo esto en su lugar:

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

No importa lo que intente, no puedo exponer la precisión total de esta columna DATE para que se devuelva a través de Java JDBC y iBATIS.

Lo que iBATIS está mapeando es esto:

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

El mapeo actual de iBATIS es este:

<result property="from_date" jdbcType="DATE" javaType="java.sql.Date"/>

También lo intenté:

<result property="from_date" jdbcType="DATETIME" javaType="java.sql.Date"/>

o

<result property="from_date" jdbcType="TIMESTAMP" javaType="java.sql.Timestamp"/>

Pero todos los intentos de mapeo producen el mismo valor de Fecha truncado. Es como si JDBC ya hubiera hecho el daño de perder precisión de datos antes de que iBATIS siquiera lo toque.

Claramente estoy perdiendo parte de la precisión de mis datos al pasar por JDBC e iBATIS lo que no sucede cuando estoy en PL / SQL Developer ejecutando el mismo fragmento de SQL como script de prueba. No es aceptable en absoluto, es muy frustrante y en última instancia da mucho miedo.


Sí, ya veo, el estándar SQL DATE simple debe ser para almacenar solo la resolución del día. De hecho, aquí hay un fragmento del tipo DATE de Oracle:

Oracle admite tanto la fecha como la hora, aunque de forma diferente al estándar SQL2. En lugar de usar dos entidades separadas, fecha y hora, Oracle solo usa una, DATE. El tipo DATE se almacena en un formato interno especial que incluye no solo el mes, el día y el año, sino también la hora, el minuto y el segundo.

Lo que hace que el DATE de Oracle exceda la DATE SQL estándar.

Mmm, la gente de Oracle PL / SQL usa DATE extensamente para mantener los valores donde dependen de la resolución para el segundo. Parece que iBATIS necesita algo así como el concepto Hibernate sql dialect, donde en lugar de interpretar DATE a través de java.sql.Date, podría anular e interpretar a través de java.util.Date, que Javadocs define como que permite una resolución de milisegundos.

Desafortunadamente cuando cambié el mapeo a algo así como:

<result property="from_date" jdbcType="DATE" javaType="java.util.Date"/>

o

<result property="from_date" jdbcType="DATETIME" javaType="java.util.Date"/>

Todavía parece que primero se tradujo SQL DATE en java.sql.Date y se perdió la precisión de la hora del día.


Descubrí cómo resolver este problema. iBATIS permite el registro de manejadores de tipo personalizado. Entonces en mi archivo sqlmap-config.xml agregué esto:

<typeAlias alias="OracleDateHandler" type="com.tideworks.ms.CustomDateHandler"/> <typeHandler callback="OracleDateHandler" jdbcType="DATETIME" javaType="date"/>

Y luego agregué esta clase que implementa la interfaz 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(String datetime) { return Timestamp.valueOf(datetime); } }

Cuando necesito mapear una FECHA de Oracle, ahora lo describo así:

<result property="from_date" jdbcType="DATETIME" javaType="date"/>


He resuelto mi problema usando jdbcType = "TIMESTAMP" en lugar de jdbcType = "DATE"

• PROBLEMA:

• SOLUCIONADO:

Saludos.

Pedro


El problema es con el controlador de Oracle.

La mejor solución que encontré fue cambiar todo jdbcType = "DATE" a jdbcType = "TIMESTAMP" y todo #column_name: DATE # a #column_name: TIMESTAMP #

Entonces cambia:

<result property="from_date" jdbcType="DATE" javaType="java.sql.Date"/>

a

<result property="from_date" jdbcType="TIMESTAMP" javaType="java.sql.Date"/>


Richard Yee menciona que los últimos controladores de Oracle solucionan el problema. Puedo confirmar eso. Tuve el mismo problema aquí con los controladores 10.2, actualizado hoy a ojdbc5.jar (11.2.0.1.0), y el problema ya no existe.


La información completa (y es más compleja que la descrita aquí y podría depender de qué versión particular de los controladores de Oracle están en uso) está en la respuesta de Richard Yee aquí - [ahora expiró el enlace a Nabble]

Toma rápida antes de que caduque de nabble ...

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

Específicamente: Tipos de datos simples ¿Qué está pasando con DATE y TIMESTAMP? Esta sección es sobre tipos de datos simples. :-)

Antes de la 9.2, los controladores JDBC de Oracle asignaban el tipo SQL DATE a java.sql.Timestamp. Esto tiene cierto sentido porque el tipo Oracle DATE SQL contiene información de fecha y hora como lo hace java.sql.Timestamp. El mapeo más obvio para java.sql.Date fue algo problemático ya que java.sql.Date no incluye información de tiempo. También fue el caso de que el RDBMS no admitía el tipo de SQL TIMESTAMP, por lo que no hubo ningún problema con el mapeo de DATE a Timestamp.

En 9.2, se agregó soporte TIMESTAMP al RDBMS. La diferencia entre DATE y TIMESTAMP es que TIMESTAMP incluye nanosegundos y DATE no. Entonces, comenzando en 9.2, DATE se asigna a Date y TIMESTAMP se mapea a Timestamp. Desafortunadamente, si confiaba en que los valores DATE contienen información de tiempo, hay un problema.

Hay varias formas de abordar este problema:

Modifique sus tablas para usar TIMESTAMP en lugar de DATE. Probablemente esto rara vez es posible, pero es la mejor solución cuando lo es.

Modifique su aplicación para usar defineColumnType para definir las columnas como TIMESTAMP en lugar de DATE. Hay problemas con esto porque realmente no desea usar defineColumnType a menos que tenga que hacerlo (consulte ¿Qué es defineColumnType y cuándo debería usarlo?).

Altere su aplicación para usar getTimestamp en lugar de getObject. Esta es una buena solución cuando es posible, sin embargo, muchas aplicaciones contienen código genérico que se basa en getObject, por lo que no siempre es posible.

Establezca la propiedad de conexión compatible con V8. Esto le dice a los controladores JDBC que utilicen la asignación anterior en lugar de la nueva. Puede establecer este indicador como una propiedad de conexión o una propiedad del sistema. Establece la propiedad de conexión agregándola al objeto java.util.Properties pasado a DriverManager.getConnection o a OracleDataSource.setConnectionProperties. Establece la propiedad del sistema incluyendo una opción -D en la línea de comandos de java.

java -Doracle.jdbc.V8Compatible = "true" MyApp Oracle JDBC 11.1 soluciona este problema. A partir de esta versión, el controlador correlaciona las columnas SQL DATE con java.sql.Timestamp de forma predeterminada. No es necesario configurar V8Compatible para obtener la asignación correcta. V8Compatible está muy desaprobado. No deberías usarlo en absoluto. Si lo configura en verdadero, no le hará daño a nada, pero debe dejar de usarlo.

A pesar de que rara vez se usaba de esa manera, V8Compatible existía para no reparar el problema DATE to Date, sino para respaldar la compatibilidad con las bases de datos 8i. Las bases de datos 8i (y anteriores) no admitían el tipo TIMESTAMP. La configuración de V8Compatible no solo ocasionó que SQL DATE se correlacione con Timestamp cuando se leyó desde la base de datos, sino que también causó que todas las marcas de tiempo se convirtieran a SQL DATE cuando se escriben en la base de datos. Como 8i no está soportado, los controladores 11.1 JDBC no son compatibles con este modo de compatibilidad. Por esta razón, V8Compatible está desprotegido.

Como se mencionó anteriormente, los controladores 11.1 de forma predeterminada convierten SQL DATE en Timestamp al leer de la base de datos. Esto siempre fue lo correcto y el cambio en 9i fue un error. Los controladores 11.1 han vuelto al comportamiento correcto. Incluso si no configuró V8Compatible en su aplicación, no debería ver ninguna diferencia en el comportamiento en la mayoría de los casos. Puede notar una diferencia si usa getObject para leer una columna de FECHA. El resultado será una marca de tiempo en lugar de una fecha. Dado que Timestamp es una subclase de Date, esto generalmente no es un problema. Donde puede notar una diferencia es si confió en la conversión de FECHA en Fecha para truncar el componente de tiempo o si lo hace en el valor. De lo contrario, el cambio debería ser transparente.

Si por alguna razón su aplicación es muy sensible a este cambio y usted simplemente debe tener el comportamiento 9i-10g, existe una propiedad de conexión que puede configurar. Establezca mapDateToTimestamp en false y el controlador volverá al comportamiento 9i-10g predeterminado y asignará DATE a Date.

Si es posible, debe cambiar su tipo de columna a TIMESTAMP en lugar de DATE.

-Ricardo

Roger Voss escribió: Publiqué la siguiente pregunta / problema en , por lo que si alguien conoce una resolución, sería bueno verla respondida allí:

Problema de conversión de Oracle SQL DATE usando iBATIS a través de Java JDBC

Aquí está la descripción del problema:

Actualmente estoy luchando con un problema de conversión de Oracle sql DATE utilizando iBATIS de Java.

Estoy usando el controlador delgado Oracle JDBC ojdbc14 versión 10.2.0.4.0. iBATIS versión 2.3.2. Java 1.6.0_10-rc2-b32.

El problema gira en torno a una columna de tipo DATE que devuelve este fragmento de SQL:

SELECCIONAR * FROM TABLE (pk_invoice_qry.get_contract_rate (?,?,?,?,?,?,?,?,?,?)) Ordenar por from_date

La llamada al procedimiento de paquete devuelve un cursor de ref que se ajusta en una TABLA donde es fácil leer el conjunto de resultados como si fuera una consulta de selección en una tabla.

En PL / SQL Developer, una de las columnas devueltas, FROM_DATE, del tipo SQL DATE, tiene precisión a la hora del día:

Tue Dec 16 23:59:00 PST 2008

Pero cuando accedo a esto a través de iBATIS y JDBC, el valor solo conserva la precisión del día:

Tue Dec 16 12:00:00 AM PST 2008

Esto es más claro cuando se muestra así:

Debería haber sido: 1229500740000 milisegundos desde la época Martes, 16 de diciembre de 2008 11:59:00 PM PST

Pero obteniendo esto en su lugar: 1229414400000 milisegundos desde época martes, 16 de diciembre de 2008 12:00:00 AM PST (como instancia de la clase java.sql.Date)

No importa lo que intente, no puedo exponer la precisión total de esta columna DATE para que se devuelva a través de Java JDBC y iBATIS.

Lo que iBATIS está mapeando es esto:

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

El mapeo actual de iBATIS es este:

También lo intenté:

o

Pero todos los intentos de mapeo producen el mismo valor de Fecha truncado. Es como si JDBC ya hubiera hecho el daño de perder precisión de datos antes de que iBATIS siquiera lo toque.

Claramente, estoy perdiendo parte de la precisión de mis datos yendo a través de JDBC e iBATIS que no está sucediendo cuando me quedo en PL / SQL Developer ejecutando el mismo fragmento de SQL como script de prueba. No es aceptable en absoluto, es muy frustrante y en última instancia da mucho miedo.