2008-12-20 26 views
13

Actualmente estoy luchando con un problema de conversión de Oracle SQL DATE usando iBATIS de Java.Oracle SQL problema de conversión de DATE utilizando iBATIS a través de Java JDBC

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 de fecha que está siendo devuelto por este fragmento de SQL:

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

la llamada a procedimiento paquete devuelve un cursor ref que se está envuelto en una tabla de donde es entonces fácil para 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, de tipo DATE SQL, tiene una 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 sólo mantiene la precisión a día:

Tue Dec 16 12:00:00 AM PST 2008 

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

debería haber sido:

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

Pero conseguir 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 intento, no puedo exponer la precisión completa de esta columna FECHA ser devueltos a través de Java JDBC y iBATIS.

Lo iBATIS es la cartografía de este es:

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

El trazado de mapas iBATIS es la siguiente:

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

También he intentado:

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

o

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

Pero todas las asignaciones intentadas producen el mismo valor de fecha truncada. 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.

Respuesta

8

La información completa (y es más complejo de lo que se describe aquí y podría depender de lo que en particular versión de los controladores de Oracle están en uso) está en answe de Richard Yee r aquí - [link ahora expirado a Nabble]


agarrar rápido antes de que caduque de nabble ...

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

en concreto: tipos de datos simples Lo que es pasando con DATE y TIMESTAMP? Esta sección trata sobre tipos de datos simples. :-)

Antes de la versión 9.2, los controladores JDBC de Oracle asignaban el tipo de DATE SQL 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 un tanto 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, el soporte TIMESTAMP se agregó 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 maneras de abordar este problema:

alterar sus tablas a utilizar en lugar de TIMESTAMP FECHA. 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 utilizar 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 corrige 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.

Aunque rara vez se usaba de esta manera, V8Compatible existía para no corregir el problema DATE to Date pero para admitir la compatibilidad con 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 desde 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 simplemente debe tener el comportamiento 9i-10g, existe una propiedad de conexión que puede establecer. 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.

-Richard


Roger Voss escribió: he publicado siguiente pregunta/problema en stackoverflow, así que si alguien sabe de una resolución, sería bueno ver que no respondió:

Oracle SQL FECHA problema de la conversión usando iBATIS a través de Java JDBC

Aquí está la descripción del problema:

estoy actualmente wres tling 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 de fecha que está siendo devuelto por este fragmento de SQL:

SELECT * de la tabla (pk_invoice_qry.get_contract_rate (,,,,,,??????? ,?,?,?)) ordenar por from_date

La llamada al procedimiento del paquete devuelve un cursor de ref que se ajusta en una TABLA donde es fácil de 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, de tipo DATE SQL, tiene una 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 sólo mantiene la precisión a día:

Tue Dec 16 12:00:00 AM PST 2008 

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

tuvo que ser: 1229500740000 milisegundos desde la época Martes, 16 de diciembre 2008 11:59:00 PM PST

Pero conseguir esto en su lugar: 1229414400000 milisegundos desde la época Martes, 16 de diciembre 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 que se devolverá a través de Java JDBC e iBATIS.

Lo iBATIS es la cartografía de este es:

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

El trazado de mapas iBATIS es la siguiente:

También he intentado:

o

Pero todos los intentos de asignaciones dió el mismo truncado Valor de fecha. 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 algo de mi precisión de datos al pasar por JDBC e iBATIS que no está sucediendo 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.

+0

el enlace no funciona más; ( –

+0

Encontró una copia y la agregó aquí ... –

+0

@Gwyn Evans Los enlaces que ha dado en respuesta están rotos. Actualícelos con los enlaces de trabajo. – OO7

0

El problema es el uso de java.sql.Date. De acuerdo con Javadoc, los valores milisegundos envueltos por una instancia 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.

0

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

Oracle es compatible con fecha y hora, aunque sea diferente de la norma SQL2 . En lugar de utilizar dos entidades independientes , fecha y hora, Oracle solo usa una, DATE. El tipo DATE se almacena en un formato interno especial que incluye no solo el mes, día y año, sino también el hora, minuto y segundo.

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

Hmm, las personas de Oracle PL/SQL usan 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.

Lamentablemente, cuando he cambiado la asignación a algo como:

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

o

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

Es todavía aparentemente primera traducidos al FECHA SQL a un java.sql.Date y perdieron el tiempo de la precisión del día.

5

Descubrí cómo solucionar este problema. iBATIS permite el registro de manejadores de tipo personalizado. Así que en mi archivo sqlmap-config.xml añadí esto:

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

Y luego añadió 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); 
    } 
} 

Whennever necesito para trazar una fecha de Oracle ahora describo que de este modo:

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

bueno, no estaba seguro de cómo superar el problema, pero me alegro de que pueda orientarlo en la dirección correcta – ninesided

+0

No puedo ver sus importaciones. Cuando está transfiriendo el valor a una Fecha, ¿eso es util o sql? – bwfrieds

2

he resuelto mi problema utilizando jdbcType = "TIMESTAMP" en lugar de jdbcType = "FECHA"

• Problema:

• resuelto:

Saludos.

Pedro

+0

+1

Resulta que el éxito de los resultados con TIMESTAMP depende de la versión del controlador JDBC de Oracle con el que se trate. Siguieron cambiando su política en cuanto a cómo traducir la precisión de milisegundos completa del PL./SQL DATE tipo a tipo java.util.Date. – RogerV

+0

Funcionó para Mi Batis 3.0.5 con ojdbc14 10.2.0.4.0 (probablemente debido a los nuevos manejadores de tipos) – fglez

1

El problema es con el controlador de Oracle.

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

Así que cambia:

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

a

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

Richard Yee menciona que los controladores más recientes de Oracle soluciona 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.

Cuestiones relacionadas