Проблема преобразования Oracle SQL DATE с использованием iBATIS через Java JDBC - PullRequest
13 голосов
/ 21 декабря 2008

В настоящее время я борюсь с проблемой преобразования 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, туда, где затем легко прочитать набор результатов, как если бы это был запрос select к таблице.

В 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"/>

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

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

Ответы [ 7 ]

8 голосов
/ 20 января 2009

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


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

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

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

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

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

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

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

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

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

Установить свойство соединения V8Compatible. Это говорит драйверам 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 для получения правильного сопоставления. Совместимость с V8 настоятельно не рекомендуется. Вы не должны использовать это вообще. Если вы установите значение true, это ничего не повредит, но вы должны прекратить его использование.

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

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

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

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

-Ричард


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

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

Вот описание проблемы:

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

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

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

ВЫБРАТЬ * FROM TABLE (pk_invoice_qry.get_contract_rate (?,?,?,?,?,?,?,?,?,?)) Заказ от from_date

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

В 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 г., 23:59 PST

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

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

Из чего отображается iBATIS:

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

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

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

* * Или тысяча семьдесят-девять

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

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

6 голосов
/ 21 декабря 2008

Я узнал, как решить эту проблему. 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 голосов
/ 18 марта 2009

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

• ПРОБЛЕМА:

• РЕШИТЬ:

Привет.

Pedro

1 голос
/ 04 сентября 2009

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

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

Так измените:

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

до

<result property="from_date" jdbcType="TIMESTAMP" javaType="java.sql.Date"/>
0 голосов
/ 06 октября 2009

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

0 голосов
/ 21 декабря 2008

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

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 и потерял точность времени суток.

0 голосов
/ 21 декабря 2008

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

...