Проблема преобразования Oracle SQL DATE с использованием iBATIS через Java JDBC

В настоящее время я борюсь с проблемой преобразования Oracle SQL DATE с помощью iBATIS из Java.

Я использую тонкий драйвер Oracle JDBC ojdbc14 версии 10.2.0.4.0. iBATIS версия 2.3.2. Java 1.6.0_10-rc2-b32.

Проблема вращается вокруг столбца типа DATE, возвращаемого этим фрагментом SQL:

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

Вызов процедуры пакета возвращает курсор ref, который обертывается в TABLE, где легко читается набор результатов, как если бы это был запрос выбора к таблице.

В PL/SQL Developer один из возвращаемых столбцов, FROM_DATE, типа SQL DATE, имеет точность до времени суток:

Tue Dec 16 23:59:00 PST 2008

Но когда я получаю доступ к этому через iBATIS и JDBC, значение сохраняет только точность до дня:

Tue Dec 16 12:00:00 AM PST 2008

Это становится яснее, когда отображается так:

Должно быть:

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

Но вместо этого получим:

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

Независимо от того, что я пытаюсь, я не могу показать полную точность этого столбца DATE, который будет возвращен через Java JDBC и iBATIS.

То, что iBATIS отображает, таково:

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

Текущее отображение iBATIS:

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

Я также пробовал:

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

или

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

Но все попытки сопоставления дают одно и то же усеченное значение даты. Как будто JDBC уже наносил ущерб потери точности данных, прежде чем iBATIS даже коснется его.

Ясно, что я теряю некоторые из своих данных, просматривая JDBC и iBATIS, чего не происходит, когда я остаюсь в PL/SQL Developer с тем же фрагментом SQL в качестве теста script. Неприемлемый вообще, очень расстраивающий, и в конечном счете очень страшный.

Ответы

Ответ 1

Полная информация (и она более сложная, чем описано здесь, и может зависеть от того, какая именно версия драйверов Oracle используется) в ответе Ричарда Йи здесь - [теперь истек срок действия ссылки на Nabble]


Быстрый захват до истечения срока его действия...

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

В частности: Простые типы данных Что происходит с DATE и TIMESTAMP? Этот раздел посвящен простым типам данных.: -)

До 9.2 драйверы JDBC Oracle сопоставили тип DATE SQL с java.sql.Timestamp. Это сделало определенный смысл, потому что тип SQL DATE SQL содержит информацию о дате и времени, как и java.sql.Timestamp. Более очевидное сопоставление с java.sql.Date было несколько проблематичным, поскольку java.sql.Date не включает информацию о времени. Кроме того, СУБД не поддерживало тип SQL TIMESTAMP, поэтому не было проблемы с отображением DATE в Timestamp.

В 9.2 поддержка СУБД TIMESTAMP была добавлена ​​в СУБД. Разница между DATE и TIMESTAMP заключается в том, что TIMESTAMP включает наносекунды, а DATE - нет. Итак, начиная с 9.2, DATE сопоставляется с Date и TIMESTAMP сопоставляется с Timestamp. К сожалению, если вы полагались на значения DATE, чтобы содержать информацию о времени, возникает проблема.

Существует несколько способов решения этой проблемы:

Измените свои таблицы, чтобы использовать TIMESTAMP вместо DATE. Это, вероятно, редко возможно, но это лучшее решение, когда оно есть.

Измените приложение, чтобы использовать defineColumnType для определения столбцов как TIMESTAMP, а не DATE. Есть проблемы с этим, потому что вы действительно не хотите использовать defineColumnType, если вам не нужно (см. Что такое defineColumnType и когда я должен его использовать?).

Измените приложение, чтобы использовать getTimestamp, а не getObject. Это хорошее решение, когда это возможно, однако многие приложения содержат общий код, который полагается на getObject, поэтому это не всегда возможно.

Задайте свойство V8Compatible connection. Это говорит о том, что драйверы JDBC используют старое сопоставление, а не новое. Вы можете установить этот флаг как свойство соединения или системное свойство. Вы устанавливаете свойство соединения, добавляя его в объект java.util.Properties, переданный в DriverManager.getConnection или в OracleDataSource.setConnectionProperties. Вы устанавливаете системное свойство, включая параметр -D в вашей командной строке java.

java -Doracle.jdbc.V8Compatible = "true" MyApp Oracle JDBC 11.1 устраняет эту проблему. Начиная с этой версии драйвер сопоставляет столбцы SQL DATE с java.sql.Timestamp по умолчанию. Нет необходимости устанавливать V8Compatible для получения правильного отображения. V8Compatible категорически не рекомендуется. Вы не должны использовать его вообще. Если вы установите значение true, это ничего не повредит, но вы должны прекратить его использовать.

Хотя он редко использовался таким образом, V8Compatible существовал не для исправления проблемы DATE to Date, а для поддержки совместимости с базами данных 8i. 8i (и более старые) базы данных не поддерживали тип TIMESTAMP. Установка V8Compatible не только заставляла SQL DATE сопоставляться с Timestamp при чтении из базы данных, но также приводила к тому, что все временные метки были преобразованы в SQL DATE при записи в базу данных. Поскольку 8i деспопортируется, 11.1 JDBC-драйверы не поддерживают этот режим совместимости. По этой причине V8Compatible не поддерживается.

Как упоминалось выше, драйверы 11.1 по умолчанию конвертируют SQL DATE в Timestamp при чтении из базы данных. Это всегда было правильным делом, и изменение в 9i было ошибкой. Драйверы 11.1 вернулись к правильному поведению. Даже если вы не установили V8Compatible в своем приложении, вы не должны видеть различий в поведении в большинстве случаев. Вы можете заметить разницу, если вы используете getObject для чтения столбца DATE. Результатом будет Timestamp, а не Date. Поскольку Timestamp является подклассом Date, это обычно не проблема. Если вы заметили, что разница заключается в том, что вы полагались на преобразование из DATE в Date, чтобы усечь компонент времени, или если вы делаете toString по значению. В противном случае изменение должно быть прозрачным.

Если по какой-то причине ваше приложение очень чувствительно к этому изменению, и вы просто должны иметь поведение 9i-10g, есть свойство соединения, которое вы можете установить. Установите mapDateToTimestamp в false, и драйвер вернется к поведению 9i-10g по умолчанию и карте DATE в Date.

Если возможно, вы должны изменить свой тип столбца на TIMESTAMP вместо DATE.

-Ричард


Роджер Восс писал: Я отправил следующий вопрос/проблему в stackoverflow, поэтому, если кто-нибудь знает разрешение, было бы хорошо, если бы он ответил там:

Проблема преобразования Oracle SQL DATE с использованием iBATIS через Java JDBC

Здесь описание проблемы:

В настоящее время я борюсь с проблемой преобразования данных SQL в Oracle с использованием iBATIS из Java.

Я использую тонкий драйвер Oracle JDBC ojdbc14 версии 10.2.0.4.0. iBATIS версия 2.3.2. Java 1.6.0_10-rc2-b32.

Проблема вращается вокруг столбца типа DATE, возвращаемого этим фрагментом SQL:

SELECT * FROM TABLE (pk_invoice_qry.get_contract_rate (?,?,?,?,?,?,?,?,?,?)) Order by_date

Вызов процедуры пакета возвращает курсор ref, который обертывается в TABLE, где легко читается набор результатов, как если бы это был запрос выбора к таблице.

В PL/SQL Developer один из возвращаемых столбцов, FROM_DATE, типа SQL DATE, имеет точность до времени суток:

Tue Dec 16 23:59:00 PST 2008

Но когда я получаю доступ к этому через iBATIS и JDBC, значение сохраняет только точность до дня:

Tue Dec 16 12:00:00 AM PST 2008

Это становится яснее, когда отображается так:

Должно быть: 1229500740000 миллисекунд с эпохи Вторник, 16 декабря 2008 г. 11:59:00 PM PST

Но вместо этого: 1229414400000 миллисекунд с эпохи Вторник, 16 декабря 2008 г. 12:00:00 AM PST (как экземпляр класса java.sql.Date)

Независимо от того, что я пытаюсь, я не могу показать полную точность этого столбца DATE, который будет возвращен через Java JDBC и iBATIS.

То, что iBATIS отображает, таково:

FROM_DATE: 2008-12-03: класс java.sql.Date

Текущее отображение iBATIS:

Я также пробовал:

или

Но все попытки сопоставления дают одно и то же усеченное значение даты. Как будто JDBC уже допустил повреждение потери точности данных, прежде чем iBATIS даже коснется его.

Ясно, что я теряю часть своей точности данных, просматривая JDBC и iBATIS, чего не происходит, когда я остаюсь в PL/SQL Developer, использующем тот же фрагмент SQL в качестве теста script. Неприемлемый вообще, очень расстраивающий, и в конечном счете очень страшный.

Ответ 2

Я узнал, как решить эту проблему. iBATIS позволяет регистрировать обработчики пользовательских типов. Поэтому в моем файле sqlmap-config.xml я добавил следующее:

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

Затем добавлен этот класс, который реализует интерфейс 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);
    }
}

Когда мне нужно сопоставить Oracle DATE, я теперь описываю его так:

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

Ответ 3

Я решил свою проблему, используя jdbcType = "TIMESTAMP" вместо jdbcType = "DATE"

• ПРОБЛЕМА:

• РЕШИТЬ:

С уважением.

Педро

Ответ 4

Проблема с драйвером Oracle.

Лучшее решение, которое я нашел, это изменить все jdbcType = "DATE" на jdbcType = "TIMESTAMP" и все #column_name: DATE # в #column_name: TIMESTAMP #

Итак, измените:

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

к

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

Ответ 5

Проблема заключается в использовании java.sql.Date. Согласно Javadoc, значения миллисекунд, завернутые экземпляром java.sql.Date, должны быть "нормализованы", установив часы, минуты, секунды и миллисекунды до нуля в конкретном часовом поясе, с которым связан экземпляр, чтобы соответствовать определению SQL DATE.

Ответ 6

Да, я вижу - простой стандарт SQL DATE должен быть только для хранения до разрешения дня. Действительно, здесь приведен фрагмент типа Oracle DATE:

Oracle поддерживает как дату, так и время, хотя и отличается от SQL2 стандарт. Вместо использования двух отдельные сущности, дата и время, Oracle использует только один, DATE. Дата тип хранится в специальной внутренней формат, который включает не только месяц, день и год, но также час, минута и секунда.

Это указывает на то, что Oracle DATE превышает стандартную SQL DATE.

Хм, люди Oracle PL/SQL используют DATE для хранения значений, в которых они зависят от разрешения, относящегося ко второму. Похоже, что iBATIS нуждается в чем-то подобном диалоговому диалекту Hibernate sql, где вместо интерпретации DATE через java.sql.Date можно переопределить и вместо этого интерпретировать через java.util.Date, который Javadocs определяет как разрешающий миллисекундное разрешение.

К сожалению, когда я изменил отображение на что-то вроде:

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

или

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

По-видимому, он сначала перевел SQL DATE на java.sql.Date и потерял точность времени суток.

Ответ 7

Ричард Йи упоминает, что последние версии драйверов Oracle исправляют проблему. Я могу это подтвердить. Здесь была проблема с драйверами 10.2, обновленная сегодня до ojdbc5.jar(11.2.0.1.0), и проблема исчезла.