【问题标题】:Hibernate 3.5 vs 4 IDENTITY_INSERT issuesHibernate 3.5 与 4 IDENTITY_INSERT 问题
【发布时间】:2012-10-16 18:54:14
【问题描述】:

我们运行 Spring 3.1/Hibernate 4/Java 7/Tomcat 7/MSSQL 2008 R2 Web 应用程序。我们必须处理遗留数据和归档数据。从存档中提取数据时,我们需要使用原始唯一标识符,以便其他(非存档)记录正确地重新水化。这些标识符存储在主键/自动增量字段中。

在此之前,当我们使用 Spring 3.0/Hibernate 3.5 时,以下代码用于将提取的记录插入到相应的表中(我们已经有变量 sessionentity 和范围):

session.doWork( new Work() 
{ 
    @Override
    public void execute(Connection connection) throws SQLException
    {
        PreparedStatement statement = null;
        try
        {
            statement = connection.prepareStatement(String.format("SET IDENTITY_INSERT %s ON", fullTableName));
            statement.execute();

            session.save(entity);

            statement = connection.prepareStatement(String.format("SET IDENTITY_INSERT %s OFF", fullTableName));
            statement.execute();
        }
        finally
        {  /* close the statement */ }
    }
});

就像我提到的,这在 Hibernate 3.5 中一切正常,但现在我们已经升级到 Hibernate 4,它已经停止工作了。 Work和IsolatedWork之间有什么区别吗?

为了解决问题并避免任何工作界面问题,我们尝试了以下方法:

session.createSQLQuery(String.format("SET IDENTITY_INSERT %s ON", fullTableName)).executeUpdate();
session.save(entity);
session.createSQLQuery(String.format("SET IDENTITY_INSERT %s OFF", fullTableName)).executeUpdate();

但是,这也不起作用。具体来说,被抛出的异常是 java.sql.SQLException: Cannot insert explicit value for identity column in table 'Employee' when IDENTITY_INSERT is set to OFF. 然而,很明显我们正在努力将其设置为 ON。

我们对情况进行了 SQL Server Profiler 跟踪,并发现了一些有趣的东西。在我们的每个交易主体中都设置了 IMPLICIT_TRANSACTIONS ON。以下是 Profiler 跟踪的一些示例输出(我已将我们的实际架构替换为 <schema>,以及一些带有较短标签的大量数据):

SET IMPLICIT_TRANSACTIONS ON
go
declare @p1 int
set @p1=55
exec sp_prepare @p1 output,N'',N'SET IDENTITY_INSERT <schema>.Employee ON',1
select @p1
go
exec sp_execute 55
go

declare @p1 int
set @p1=56
exec sp_prepare @p1 output,N'<parameters for the INSERT>',N'insert into <schema>.Employee (<all the column names>) values ( <all the parameters> )',1
select @p1
go
exec sp_execute 56,<the actual values to insert>
go
IF @@TRANCOUNT > 0 ROLLBACK TRAN
go
IF @@TRANCOUNT > 0 COMMIT TRAN
SET IMPLICIT_TRANSACTIONS OFF
go
exec sp_execute 54,N'Error writing EMPLOYEE archive record. ',<an id>,N'1'
go

现在,我们通过事务中的 Connection.setAutoCommit(false) 专门将 IMPLICIT_TRANSACTIONS 设置为 OFF(通过 Spring @Transactional 和 Hibernate Transaction Manager 管理事务)。显然,这不起作用,但是除了使用 setAutoCommit 之外还有什么替代方法,为什么它会在 Spring3.0/Hibernate 3.5 中起作用,而在 Spring 3.1/Hibernate 4 中不起作用?

感谢您的任何想法或建议 - 我们很难过。

【问题讨论】:

    标签: hibernate transactions implicit identity-insert


    【解决方案1】:

    嗯,这是一个微妙的解决方案......

    我们的工作调用在内部使用了java.sql.PreparedStatement,然后我们调用了execute() 方法。显然,这告诉 SQL Server 将命令包装在自己的存储过程中,如一些代码示例所示。

    我们从使用PreparedStatement 改为简单的java.sql.Statement 并调用它的execute() 方法:

    session.doWork( new Work() 
    { 
        @Override
        public void execute(Connection connection) throws SQLException
        {
            Statement statement = null;
            try
            {
                statement = connection.createStatement();
                statement.execute(String.format("SET IDENTITY_INSERT %s ON", fullTableName));
    
                session.save(entity);
    
                statement = connection.createStatement();
                statement.execute(String.format("SET IDENTITY_INSERT %s OFF", fullTableName));
            }
            finally
            {  /* close the statement */ }
        }
    });
    

    那么,有什么区别呢?据我们所知,PreparedStatement 生成预编译的 SQL,而 Statement 生成静态 SQL...正是我们调用 IDENTITY_INSERT 所需要的!

    教训:人渣和恶棍的蜂巢很多......我们必须小心!

    【讨论】:

    • 非常感谢,这确实为我节省了很多时间。
    猜你喜欢
    • 2010-10-01
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    • 2015-09-21
    • 2014-03-03
    • 1970-01-01
    • 2014-10-07
    • 1970-01-01
    相关资源
    最近更新 更多