【问题标题】:JDBC Prepared Statement Confused about session scopeJDBC Prepared Statement 对会话范围感到困惑
【发布时间】:2018-05-10 22:47:42
【问题描述】:

我在升级 JDBC 驱动程序时遇到了问题。我尝试了两种不同的驱动程序 JNetDirects JSQLConnect 和 Microsoft 驱动程序都显示相同的行为。在非自动提交状态下执行多个准备好的语句时,这些语句似乎没有共享会话状态。这给我带来了麻烦。有什么方法可以指示语句应该共享相同的会话状态的连接?

这是一个如何复制不共享会话状态的示例。以下片段在insert.execute(); 行引发异常。身份插入关闭导致的异常表明两个准备好的语句之间未维护会话状态。

Connection connection = dataSource.getConnection();
connection.setAutoCommit(false);
PreparedStatement identityON = connection.prepareStatement("SET IDENTITY_INSERT TestStuff ON");
identityON.execute();
identityON.close();

PreparedStatement insert = connection.prepareStatement("INSERT INTO TestStuff (id) VALUES(-1)");
insert.execute(); // Results in Cannot insert explicit value for identity column in table 'TestStuff' when IDENTITY_INSERT is set to OFF. 
insert.close();

PreparedStatement identityOFF = connection.prepareStatement("SET IDENTITY_INSERT TestStuff OFF");
identityOFF.execute();
identityOFF.close();

connection.commit();

connection.close();

表创建:

CREATE TABLE TestStuff (
    id int identity(1,1) PRIMARY KEY
    ,col int
)

在排除可能有问题的行为时,我确保不会在批次之间清除会话状态

SET IDENTITY_INSERT TestStuff ON:
GO
INSERT INTO TestStuff (id) VALUES(-1);
GO
SET IDENTITY_INSERT TestStuff OFF:

这将在直接针对 SQL Server 实例执行时起作用。证明批处理不会影响会话范围。

另一个好奇是 @@IDENTITY 将在语句之间进行,但 SCOPE_IDENTITY() 不会。

    PreparedStatement insert = connection.prepareStatement("INSERT INTO TestStuff (Col) VALUES(1)");
    insert.execute();
    insert.close();

    PreparedStatement scoptIdentStatement = connection.prepareStatement("SELECT @@IDENTITY, SCOPE_IDENTITY()");
    scoptIdentStatement.execute();
    ResultSet scoptIdentRS = scoptIdentStatement.getResultSet();
    scoptIdentRS.next();
    Short identity = scoptIdentRS.getShort(1);
    Short scopeIdent = scoptIdentRS.getShort(2);

    PreparedStatement maxIdStatement = connection.prepareStatement("SELECT MAX(id) FROM TestStuff");
    maxIdStatement.execute();
    ResultSet maxIdRS = maxIdStatement.getResultSet();
    maxIdRS.next();
    Short actual = maxIdRS.getShort(1);

    System.out.println(String.format("Session: %s Scope: %s, Actual: %s", identity, scopeIdent, actual )); // Session: 121 Scope: 0, Actual: 121

SQL Server 中的相同示例和结果:

INSERT INTO TestStuff( col) VALUES (1)
PRINT CONCAT('Session: ', @@IDENTITY, ' Scope: ', SCOPE_IDENTITY() ) 
-- Session: 122 Scope: 122 (Can't print actual without polluting the output here)

【问题讨论】:

  • “给我带来麻烦” 这应该是对您问题的描述吗? --- idownvotedbecau.se/itsnotworking
  • @Andreas 代码无法提交。它在插入时引发异常。代码是演示问题的片段,而不是完整的程序。
  • 对不起,我添加了错误的链接。这是正确的:idownvotedbecau.se/noexceptiondetails --- 不要解释异常。显示它,包括。堆栈跟踪!
  • 第二个例子从不同的角度说明了这个问题。两者都是同一个问题,但以不同的方式表现出来。 @JavaDevil
  • 您可能需要考虑在 JDBC 中使用生成的密钥检索支持,而不是依赖 @@IDENTITYSCOPE_IDENTITY

标签: java sql tsql jdbc prepared-statement


【解决方案1】:

如果您将PreparedStatement 更改为Statement for IDENTITY_INSERT 设置,那么这将按您的意愿工作

Statement identityON = connection.createStatement();
identityON.execute("SET IDENTITY_INSERT TestStuff ON");
identityON.close();

另请注意docs

SCOPE_IDENTITY:返回插入到同一范围内的标识列中的最后一个标识值。范围是一个模块:存储过程、触发器、函数或批处理。因此,如果两条语句在同一个存储过程、函数或批处理中,它们就在同一个作用域中。

SCOPE_IDENTITY 和@@IDENTITY 返回在当前会话的任何表中生成的最后一个标识值。但是,SCOPE_IDENTITY 返回仅在当前范围内插入的值; @@IDENTITY 不限于特定范围。

因此 Management Studio 和 JDBC 驱动程序的值不同。

【讨论】:

  • 这改变了问题域。语句不能接受参数,使其无法启动。中间语句必须保持为准备好的语句。
  • 是的,它可以而且它仍然有效。正如我提到的,它只需要是IDENTITY_INSERT 的设置,它需要是Statement
猜你喜欢
  • 2014-07-08
  • 2017-09-20
  • 1970-01-01
  • 1970-01-01
  • 2011-07-17
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多