Java JDBC编写的语句混淆了会话范围

Java JDBC编写的语句混淆了会话范围,java,sql,tsql,jdbc,prepared-statement,Java,Sql,Tsql,Jdbc,Prepared Statement,我在升级JDBC驱动程序时遇到了一个问题。我试过两种不同的司机 JnetJSQLConnect和Microsofts驱动程序都显示相同的行为。在非自动提交状态下执行多个准备好的语句时,这些语句似乎不共享会话状态。这给我带来了麻烦。有没有办法指示连接语句应该共享相同的会话状态 下面是一个如何复制不共享会话状态的示例。以下片段在第insert.execute()行抛出异常。由于identity insert处于关闭状态而导致的异常,表明在两个准备好的语句之间未维护会话状态 Connection co

我在升级JDBC驱动程序时遇到了一个问题。我试过两种不同的司机 JnetJSQLConnect和Microsofts驱动程序都显示相同的行为。在非自动提交状态下执行多个准备好的语句时,这些语句似乎不共享会话状态。这给我带来了麻烦。有没有办法指示连接语句应该共享相同的会话状态

下面是一个如何复制不共享会话状态的示例。以下片段在第
insert.execute()行抛出异常。由于identity insert处于关闭状态而导致的异常,表明在两个准备好的语句之间未维护会话状态

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)

如果将
IDENTITY\u INSERT
设置的
PreparedStatement
更改为
Statement
,则这将按您的意愿工作

Statement identityON = connection.createStatement();
identityON.execute("SET IDENTITY_INSERT TestStuff ON");
identityON.close();
另请注意:

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

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


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

“给我带来麻烦”应该是对您的问题的描述吗@Andreas代码无法提交到提交。它在insert上抛出一个异常。代码是一个演示问题的片段,不是一个完整的程序。对不起,我添加了错误的链接。下面是正确的一个:---不要解释这个例外。显示它,包括stacktrace!第二个例子从不同的角度说明了这个问题。两者都是同一个问题,但表现方式不同@JavaDaviLu可能想考虑在JDBC中使用生成的密钥检索支持,而不是依赖于<代码> @标识或<代码> SisteIONIONIO/CODE>。这会改变问题域。语句不能接受使其成为非starter的参数。中间的语句必须是一个预先准备好的语句。是的,它可以而且仍然有效。正如我提到的,它只需要设置
IDENTITY\u INSERT
,它需要是一个
语句