2012-10-16 10 views
7

Eseguiamo un'applicazione Web Spring 3.1/Hibernate 4/Java 7/Tomcat 7/MSSQL 2008 R2. Dobbiamo occuparci di dati legacy e dati archiviati. Quando estraiamo i dati da un archivio, abbiamo bisogno di utilizzare l'identificatore univoco originale in modo che altri record (non archiviati) si reidratino correttamente. Questi identificativi sono memorizzati nel campo chiave primaria/incremento automatico.Hibernate 3.5 vs 4 problemi IDENTITY_INSERT

Prima di ora, quando usavamo Primavera 3.0/Hibernate 3.5, il seguente codice lavorato per inserire un record di estratto di nuovo nella sua tabella appropriata (abbiamo già le variabili session, entity, e fullTableName a portata):

Come ho già detto, tutto ha funzionato bene in Hibernate 3.5, ma ora che abbiamo aggiornato a Hibernate 4, ha smesso di funzionare. C'è qualcosa con la differenza tra Work e IsolatedWork?

Nel tentativo di risolvere il problema, e di evitare eventuali problemi di interfaccia lavoro, abbiamo provato il seguente:

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

Tuttavia, questo non ha funzionato neanche. Nello specifico, l'eccezione che viene generata è java.sql.SQLException: Cannot insert explicit value for identity column in table 'Employee' when IDENTITY_INSERT is set to OFF. Tuttavia, dovrebbe essere chiaro che stiamo attraversando dei problemi per impostarlo.

Abbiamo tracciato una traccia della situazione di SQL Server Profiler e trovato qualcosa di interessante. Qualcosa sta impostando IMPLICIT_TRANSACTIONS su ciascuno dei nostri corpi delle transazioni. Ecco qualche esempio di output dalla traccia di Profiler (ho sostituito il nostro schema vero e proprio con <schema>, e alcuni grandi bit di dati con etichette più brevi):

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 

Ora, stiamo specificamente allestendo IMPLICIT_TRANSACTIONS di essere fuori, mediante la connessione .setAutoCommit (false) nella nostra transazione (le transazioni vengono gestite tramite Spring @Transactional e Hibernate Transaction Manager). Ovviamente, questo non funziona, ma quali sono le alternative oltre all'uso di setAutoCommit e perché dovrebbe funzionare in Spring3.0/Hibernate 3.5 ma non Spring 3.1/Hibernate 4?

Grazie per qualsiasi pensiero o suggerimento - siamo perplessi.

risposta

2

Beh, è ​​stata una soluzione sottile ...

La nostra chiamata di lavoro ha utilizzato un java.sql.PreparedStatement internamente, e poi abbiamo chiamato il metodo execute(). Apparentemente, questo indica a SQL Server di includere il comando nella propria stored procedure, come mostrano alcuni esempi di codice.

abbiamo cambiato l'uso di un PreparedStatement semplicemente un java.sql.Statement e chiamando il suo metodo 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 */ } 
    } 
}); 

Quindi, qual è la differenza? Per quanto ne sappiamo, lo PreparedStatement genera SQL precompilato, mentre lo Statement genera SQL statico ... esattamente ciò di cui avevamo bisogno per la chiamata a IDENTITY_INSERT!

Lezione: ci sono molti alveari di feccia e malvagità ... dobbiamo essere prudenti!

+0

Grazie mille, questo mi ha fatto risparmiare un sacco di tempo. –