2012-10-16 24 views
7

Chúng tôi chạy ứng dụng web Spring 3.1/Hibernate 4/Java 7/Tomcat 7/MSSQL 2008 R2. Chúng ta phải xử lý dữ liệu cũ và dữ liệu đã lưu trữ. Khi trích xuất dữ liệu từ một kho lưu trữ, chúng tôi có nhu cầu sử dụng số nhận dạng duy nhất ban đầu để các bản ghi khác (không được lưu trữ) sẽ tái hydrate một cách chính xác. Các số nhận dạng này được lưu trữ trong trường khóa chính/tăng tự động.Hibernate 3.5 so với 4 sự cố IDENTITY_INSERT

Trước đến nay, khi chúng tôi đang sử dụng Spring 3,0/Hibernate 3.5, các mã sau đây làm việc để chèn một kỷ lục chiết xuất trở lại vào bảng thích hợp của nó (chúng tôi đã có các biến session, entity, và fullTableName trong phạm vi):

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 */ } 
    } 
}); 

Như tôi đã đề cập, tất cả đều hoạt động tốt trong Hibernate 3.5, nhưng bây giờ chúng tôi đã nâng cấp lên Hibernate 4, nó đã ngừng hoạt động. Có điều gì đó với sự khác biệt giữa Công việc và IsolatedWork không?

Trong một nỗ lực để giải quyết vấn đề, và tránh bất kỳ vấn đề giao diện làm việc, chúng tôi thử như sau:

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(); 

Tuy nhiên, điều này đã không làm việc một trong hai. Cụ thể, ngoại lệ được ném là java.sql.SQLException: Cannot insert explicit value for identity column in table 'Employee' when IDENTITY_INSERT is set to OFF. Tuy nhiên, cần phải rõ ràng rằng chúng ta đang phải trải qua những cơn đau để thiết lập BẬT.

Chúng tôi đã thực hiện theo dõi SQL Server Profiler của tình huống và tìm thấy điều gì đó thú vị. Một cái gì đó đang đặt IMPLICIT_TRANSACTIONS ON trong mỗi cơ quan giao dịch của chúng tôi. Dưới đây là một số lượng mẫu từ các dấu vết Profiler (Tôi đã thay thế sơ đồ của chúng tôi thực tế với <schema>, và một số bit dữ liệu lớn với nhãn ngắn hơn):

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 

Bây giờ, chúng tôi đang thiết lập đặc biệt IMPLICIT_TRANSACTIONS là OFF, thông qua kết nối .setAutoCommit (false) trong giao dịch của chúng tôi (các giao dịch đang được quản lý thông qua Spring @Transactional và Hibernate Transaction Manager). Rõ ràng, đó không phải là làm việc, nhưng các lựa chọn thay thế ngoài việc sử dụng setAutoCommit là gì, và tại sao nó sẽ làm việc trong Spring3.0/Hibernate 3.5 nhưng không phải Spring 3.1/Hibernate 4?

Cảm ơn mọi suy nghĩ hoặc đề xuất - chúng tôi đang bối rối.

Trả lời

2

Vâng, đó là một giải pháp tinh tế ...

cuộc gọi công việc của chúng tôi sử dụng một java.sql.PreparedStatement nội bộ, và sau đó chúng ta gọi là phương pháp execute(). Rõ ràng, điều này cho SQL Server để bọc lệnh trong thủ tục lưu sẵn của nó, như một số mẫu mã hiển thị.

Chúng tôi đã thay đổi từ việc sử dụng một PreparedStatement chỉ đơn giản là một java.sql.Statement và gọi phương thức execute() của nó:

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 */ } 
    } 
}); 

Vì vậy, sự khác biệt là gì? Theo như chúng tôi có thể nói, PreparedStatement tạo SQL được biên dịch trước, trong khi Statement tạo SQL tĩnh ... chính xác những gì chúng tôi cần cho cuộc gọi đến IDENTITY_INSERT!

Bài học: có rất nhiều phát ban của cặn bã và kẻ phản diện ... chúng ta phải thận trọng!

+0

Cảm ơn bạn rất nhiều, chắc chắn điều này đã giúp tôi tiết kiệm rất nhiều thời gian. –

Các vấn đề liên quan