【问题标题】:Why is my datetime parameter for a stored procedure being rejected?为什么我的存储过程的日期时间参数被拒绝?
【发布时间】:2012-03-20 10:49:57
【问题描述】:

我正在开发一个 Java 服务器应用程序,该应用程序使用 Spring 3 和 C3P0 访问 Microsoft SQL Server 2008 R2 数据库,并使用 Microsoft JDBC 4 驱动程序 3.0 版。

我有一个存储过程,它的输入定义如下:

@modifiedAfter datetime = NULL

我正在使用 Spring 来构造对这个存储过程的调用。

我正在构建一个 MapSqlParameterSource 来包含我的参数:

MapSqlParameterSource in = new MapSqlParameterSource()
in.addValue("modifiedAfter", "2011-01-01T00:00:00", Types.TIMESTAMP)

但是当我执行调用时:

this.sprocCall.execute(in);

我明白了:

com.microsoft.sqlserver.jdbc.SQLServerException: 从字符串转换日期和/或时间时转换失败

……我不知道为什么。

我尝试了一些添加参数的变体,例如将其作为 Date 传递,或将其指定为 VARCHAR,或不指定类型 - 它们都不起作用。

我开始怀疑这个问题可能与 Spring 有关。我写了一个小 Groovy 脚本来尝试隔离问题,这很好用:

dt = new DateTime("2012-02-01T00:00:00") // Joda DateTime
println sql.rows("exec spMySproc @modifiedAfter=${Sql.TIMESTAMP(dt.toString())}")

...但是当我尝试使用 MapSqlParameterSource 的等效方法时,我得到了上述错误。

在这一点上,我被难住了。

这是堆栈跟踪的顶部:

org.springframework.jdbc.BadSqlGrammarException: CallableStatementCallback; bad SQL grammar [{call spGetPoliciesCatalogPaged(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}]; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: Conversion failed when converting date and/or time from character string.
    at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:97)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:952)
    at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:985)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:368)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:342)
    at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:164)

…后面是我的一些课程,然后是:

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Conversion failed when converting date and/or time from character string.
    at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:197)
    at com.microsoft.sqlserver.jdbc.SQLServerResultSet$FetchBuffer.nextRow(SQLServerResultSet.java:4762)
    at com.microsoft.sqlserver.jdbc.SQLServerResultSet.fetchBufferNext(SQLServerResultSet.java:1682)
    at com.microsoft.sqlserver.jdbc.SQLServerResultSet.next(SQLServerResultSet.java:955)
    at com.mchange.v2.c3p0.impl.NewProxyResultSet.next(NewProxyResultSet.java:2859)
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:91)
    at org.springframework.jdbc.core.JdbcTemplate.processResultSet(JdbcTemplate.java:1124)
    at org.springframework.jdbc.core.JdbcTemplate.extractReturnedResults(JdbcTemplate.java:1023)
    at org.springframework.jdbc.core.JdbcTemplate$5.doInCallableStatement(JdbcTemplate.java:995)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:936)
    ... 68 more

我尝试更新到最新版本的spring-jdbc,3.1.1,但没有帮助。

如果有任何帮助,我将不胜感激!

谢谢, 视频

【问题讨论】:

  • 你试过用字符串代替 Types.TIMESTAMP 吗?我认为Types.TIMESTAMP格式不能在sql server中转换。
  • SQL Server 中的TIMESTAMP 与日期或时间完全无关。
  • @ThitLwinOo, @Aaron:我没有使用 SQL Server timestamp 格式。我使用的是 JDBC TIMESTAMP 格式。根据this page,JDBC 驱动程序将JDBC TIMESTAMP 格式映射到SQL Server 类型datetimedatetime2。此外,上面使用 JDBC TIMESTAMP 的 Groovy 脚本也可以正常工作。

标签: java sql-server spring stored-procedures jdbc


【解决方案1】:

在 @MSDN 中遇到了类似的问题,并且看起来 Sql-Server 2005 存在精确转换的错误。据说您使用的版本提高了 100ns 的精度。是否可以验证时间戳精度。以下是讨论链接:Sql Server Conversion error

【讨论】:

  • 我有问题的示例代码具有硬编码的值:in.addValue("modifiedAfter", "2011-01-01T00:00:00", Types.TIMESTAMP),因此您可以看到标准 SQL Server DateTime 无法表示的精度级别的数据。
  • in.addValue("modifiedAfter", "2011-01-01T00:00:00", Types.String),那个怎么样?因为“2011-01-01T00:00:00”这个值在sql server中转换是没有问题的。
  • @ThitLwinOo Types.String 不存在。您可以看到所有 JDBC 类型的列表here。我试过Types.VARCHAR,我得到了同样的错误。
【解决方案2】:

我的一位同事发现了这一点。事实证明,我已经过分指责 Spring 和/或 Microsoft SQL Server 驱动程序。将字符串转换为日期/时间时,问题确实是一个错误——但这不是我的参数的直接问题,而是由我的 sproc 生成的动态 SQL 运行的查询的问题,如果这个参数我传入了。

有问题的代码如下所示:

IF @modifiedAfter IS NOT NULL BEGIN
    SET @SQL = @SQL + N'AND mytable.ThingLastUpdated > @modifiedAfter'
END

@SQL 中包含的动态SQL 实际执行后,将@modifiedAfter 的内容——一个完全有效的DATETIME,从我的Java 代码中毫无问题地发送——与@987654325 的内容进行比较@ - 这是一个 VARCHAR 列,这意味着 SQL Server 必须将其内容转换为 DATETIME 以便它们可以与 @modifiedAfter 进行比较 - 但并非 mytable.ThingLastUpdated 中的所有值都可以成功转换,因此转换抛出错误。

我决定的解决方法是将mytable.ThingLastUpdated 的类型更改为DATETIME,并在插入时进行任何需要的转换,我认为这比在查询时这样做更有效率。

经验教训:如果 SQL 问题不在 my Java 代码中,它可能在 my SQL 中——而不是在 Spring 或 JDBC 驱动程序中。 PEBKAC。

【讨论】:

    【解决方案3】:

    这个错误肯定不是 SQL 问题,因为它成功地将字符串“2011-01-01T00:00:00”转换为日期时间。您声明的 sql 参数类型是 TIMESTAMP 类型,并且您正在传递一个字符串。我认为这就是它无法转换的地方。

    尝试将 sql 参数类型声明为 string 或 varchar 而不是 TimeStamp。

    【讨论】:

    • 正如我在问题中提到的,我已经尝试过了。它不起作用。
    • 你的意思是作为值还是作为指定类型?需要明确的是,我很确定这里的问题出在 Spring 上——我非常清楚我正在尝试做的事情的语义应该得到 JDBC 和 MSSQL 的支持。无论如何,我已经尝试过各种值和指定类型的组合——Java Date 对象、Types.VARCHAR 等。此时我相信 Spring 正在内省存储过程,看到参数被声明为 DATETIME,并尝试将值强制为兼容的东西——并且由于某种原因而失败。我想我得深入研究一下 Spring 的源代码了……
    【解决方案4】:

    如何将输入作为 String/varchar 传递,然后在存储过程中将其转换为日期时间?

    【讨论】:

    • 虽然我认为这可能是一个可行的解决方法,并且我感谢您的帮助,但解决方法不是我正在寻找的——我想了解为什么我的代码,应该 i> 工作,不工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-01
    相关资源
    最近更新 更多