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

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

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

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

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

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

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

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)

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

7 ответов


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


быстрый захват, прежде чем он истекает из nabble...

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

в частности: простой тип данных Что происходит с датой и Метка? Этот раздел посвящен простым типам данных. :-)

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

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

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

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

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

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

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

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

хотя он редко использовался таким образом, V8Compatible существовал не для исправления проблемы с датой, а для поддержки совместимости с базами данных 8i. 8i (и более старые) базы данных не поддерживают тип метки времени. Установка V8Compatible не только привела к сопоставлению даты SQL с меткой времени при чтении из базы данных, но и к преобразованию всех меток времени в дату SQL при записи в базу данных. С 8и-это desupported, в 11,1 драйверы JDBC не поддерживает этот режим совместимости. По этой причине V8Compatible является desupported.

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

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

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

-Ричард


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

проблема преобразования даты Oracle SQL с использованием iBATIS через Java В JDBC

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

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

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

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

выбрать * Из таблицы (pk_invoice_qry.get_contract_rate(?,?,?,?,?,?,?,?,?,?)) порядок from_date

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

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

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 PST

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

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

то, что iBATIS отображает из это:

FROM_DATE : 2008-12-03 : класс java.язык SQL.Дата

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

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

или

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

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


я узнал, как решить эту проблему. 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, я теперь описываю ее так:

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

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

• проблемы:

•решена:

с уважением.

Педро


проблема связана с драйвером Oracle.

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

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

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

to

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

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


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

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

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

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

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

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

или

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

это все еще, по-видимому, первый перевод даты SQL на java.язык SQL.Дата и потерял точность времени суток.


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