SQL Server 2016 DB

时间:2018-01-24 12:02:31

标签: sql-server hibernate datetime jtds mssql-jdbc

我们最近从SQL Server 2008升级到SQL Server 2016,一个特定的Hibernate / JPA错误在我们的雷达之下,让我们现在非常头疼。

我们正在使用Java和Hibernate(JPA 2.1)来与数据库进行交互。

问题如下:

我们有一个表格,其中包含 datetime 类型的列。在我们的Java程序结束时,我们通过hibernate执行命名更新查询,其中我们在where子句中对此列有一个条件:

datetime-column < :parameter.

datetime列始终映射到相应实体中的java.util.Date。传递的参数也是java.util.Date类型。

然而,在升级之前我们使用的是jTDS驱动程序(一切都运行正常:))但升级后我们决定切换到官方的微软驱动程序。我们做了这个举动是因为社区似乎表明微软多年来改进并维护了他们的驱动程序,而JTDS驱动程序多年来一直没有维护......

我们现在面临的问题是,使用MSSSQL驱动程序,提供的java.util.Date参数将作为 datetime2 而不是 datetime 传递(如JTDS驱动程序所做的那样)。

因此,updatequery返回非确定性结果(类似于这篇文章:JDBC driver sends Timestamp as datetime2 causing where clauses to fail),这就是为什么这个bug在我们的雷达之下。

我尝试通过将org.hibernate.type的loglevel设置为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, 

如果我们查看执行wuth JTDS的相同查询,我们会看到日期时间参数被传递而不是datetime2。

我现在的问题是:我们不想将datetime列更新为datetime2。有没有其他方法来配置hibernate,以便将date参数作为datetime发送到db而不是datetime2?

有人可以指出我们正确的方向吗?我尝试了一切,包括将数据类型更改为日历,实现此处所述的自定义UserType(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);
        }

    }
}

1 个答案:

答案 0 :(得分:0)

您是否找到了使用纯JDBC的解决方案?

您是否尝试过以下方法:

的PreparedStatement     default void setObject(int parameterIndex,Object x,SQLType targetSqlType)

可能存在特定于供应商的SQLType。