Проблемы с драйвером JDBC Hibernate JTDS / MSSQL со столбцом datetime в базе данных SQL Server 2016

мы недавно обновили SQL Server 2008 до SQL Server 2016, и конкретная ошибка Hibernate / JPA осталась незамеченной и сейчас вызывает у нас головную боль.

Мы используем Java с Hibernate (JPA 2.1) для взаимодействия с БД.

Проблема в следующем:

У нас есть таблица со столбцом типа datetime. В конце нашей Java-программы мы выполняем именованный-update-query через спящий режим, где у нас есть условие для этого столбца в предложении where:

datetime-column < :parameter.

Столбец datetime всегда был сопоставлен с java.util.Date в соответствующем Entity. Переданный параметр также имеет тип java.util.Date.

Однако до обновления мы использовали драйвер jTDS (и все работало нормально :)), но после обновления мы решили перейти на официальный драйвер Microsoft. Мы сделали этот шаг, потому что сообщество, похоже, указывало, что Microsoft улучшала и поддерживала свой драйвер на протяжении многих лет, тогда как драйвер JTDS не поддерживался годами ...

Теперь мы сталкиваемся с проблемой, заключающейся в том, что с драйвером MSSSQL предоставленный параметр java.util.Date передается как datetime2 вместо datetime (как это делал драйвер JTDS).

Из-за этого запрос обновления возвращает недетерминированные результаты (аналогичные этому сообщению: Драйвер JDBC отправляет метку времени как datetime2, что приводит к сбою предложений where), поэтому эта ошибка осталась незамеченной.

Я попытался зарегистрировать фактическое утверждение, установив уровень журнала для org.hibernate.type на TRACE и даже использовал https://github.com/p6spy/p6spy, чтобы регистрировать фактические утверждения, но только когда мы профилировали сервер БД через SSMS, мы говорим о реальной проблеме:

exec sp_prepexec @p1 output,N'@P0 datetime2,@P1 datetime2',N'update PRODUCT 
set MARKETING_TEXT=null, version=version+1, 

Если мы посмотрим на тот же запрос, выполняемый через JTDS, мы увидим, что параметры datetime передаются вместо datetime2.

У меня вопрос: мы не хотим обновлять столбец datetime до datetime2. Есть ли другой способ настроить спящий режим таким образом, чтобы он отправлял параметр даты как datetime в db вместо datetime2?

Может кто-нибудь указать нам правильное направление? Я пробовал все, включая изменение типа данных на календарь, реализацию пользовательского типа пользователя, как описано здесь (https://blogs.sourceallies.com/2012/02/hibernate-date-vs-timestamp/), удаляя / изменяя временную аннотацию и даже пытаясь настроить диалект SQL Server для принудительной передачи datetime вместо datetime2.

До сих пор ничего не работало (или я ошибся), есть предложения?

ОБНОВЛЕНИЕ 25.01.2018:

Чтобы быть более понятным, я пробовал это:

public class CustomSQLServerDialect extends SQLServer2012Dialect {

    public CustomSQLServerDialect() {

        registerColumnType(Types.TIMESTAMP, "datetime");
    }
}

и определение настраиваемого типа пользователя, подобного этому (если честно, копия вставлена ​​из сообщения с аналогичной проблемой):

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Date;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;

public class DateTimeUserType implements UserType {

    @Override
    public int[] sqlTypes() {
        return new int[] { Types.TIMESTAMP };
    }

    @SuppressWarnings("rawtypes")
    @Override
    public Class returnedClass() {
        return Date.class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return x == y || !(x == null || y == null) && x.equals(y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
            throws HibernateException, SQLException {
        Timestamp timestamp = rs.getTimestamp(names[0]);
        if (rs.wasNull()) {
            return null;
        }
        return new Date(timestamp.getTime());
    }


    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
            throws HibernateException, SQLException {
        if (value == null) {
            st.setNull(index, Types.TIMESTAMP);
        } else {
            Date date = (Date) value;
            Timestamp timestamp = new Timestamp(date.getTime());
            st.setTimestamp(index, timestamp);
        }

    }
}

person Kim Zeevaarders    schedule 24.01.2018    source источник


Ответы (1)


Вы нашли решение, используя чистый JDBC?

Вы пробовали такие методы, как:

PreparedStatement по умолчанию void setObject (int parameterIndex, Object x, SQLType targetSqlType)

Вероятно, существует SQLType, зависящий от поставщика.

person B_St    schedule 24.01.2018
comment
спасибо за обратную связь, я попробую реализовать запрос с помощью JDBC, а затем отлажу драйвер, чтобы увидеть, какой SQLType работает. - person Kim Zeevaarders; 25.01.2018
comment
Я отладил запрос, и это похоже на проблему преобразования данных в методе PreparedStatement.setTimestamp () драйвера microsoft. Эта проблема также обсуждается здесь: github.com/Microsoft/mssql-jdbc/issues/443. В настоящее время исправление недоступно, хотя кто-то назначил билет на следующий этап задачи 20 дней назад. Кажется, этот баг очень свежий :) - person Kim Zeevaarders; 13.02.2018