NHibernate ISession Flush: Onde e quando usá-lo e por quê?

187

Uma das coisas que me deixa completamente confuso é o uso de session.Flush, em conjunto com session.Commit, e session.Close.

Às vezes session.Closefunciona, por exemplo, confirma todas as alterações necessárias. Sei que preciso usar o commit quando tenho uma transação ou uma unidade de trabalho com várias criações / atualizações / exclusões, para que eu possa optar por reverter se ocorrer um erro.

Mas, às vezes, fico realmente frustrado com a lógica por trás session.Flush. Já vi exemplos em que você session.SaveOrUpdate()segue um flush, mas quando removo o flush, ele funciona bem de qualquer maneira. Às vezes, encontro erros na instrução Flush dizendo que a sessão atingiu o tempo limite e a remoção garantiu que não encontrei esse erro.

Alguém tem uma boa orientação sobre onde ou quando usar um Flush? Verifiquei a documentação do NHibernate para isso, mas ainda não consigo encontrar uma resposta direta.

Jon Limjap
fonte

Respostas:

236

Resumidamente:

  1. Sempre use transações
  2. Não use Close(), em vez disso, agrupe suas chamadas em uma instrução ISessioninterna usingou gerencie o ciclo de vida da sua ISession em outro lugar .

A partir da documentação :

De tempos em tempos, ISessionele executa as instruções SQL necessárias para sincronizar o estado da conexão ADO.NET com o estado dos objetos mantidos na memória. Esse processo, liberado, ocorre por padrão nos seguintes pontos

  • de algumas invocações de Find()ouEnumerable()
  • de NHibernate.ITransaction.Commit()
  • de ISession.Flush()

As instruções SQL são emitidas na seguinte ordem

  1. todas as inserções de entidade, na mesma ordem em que os objetos correspondentes foram salvos usando ISession.Save()
  2. todas as atualizações da entidade
  3. todas as exclusões de coleção
  4. todas as exclusões, atualizações e inserções de elementos de coleção
  5. todas as inserções de coleção
  6. todas as exclusões de entidades, na mesma ordem em que os objetos correspondentes foram excluídos usando ISession.Delete()

(Uma exceção é que os objetos que usam geração de ID nativa são inseridos quando são salvos.)

Exceto quando você explica Flush(), não há absolutamente nenhuma garantia sobre quando a Sessão executa as chamadas do ADO.NET, apenas a ordem em que elas são executadas . No entanto, o NHibernate garante que os ISession.Find(..)métodos nunca retornarão dados obsoletos; nem retornarão os dados errados.

É possível alterar o comportamento padrão para que a liberação ocorra com menos frequência. A FlushModeclasse define três modos diferentes: liberar apenas no momento da confirmação (e somente quando a ITransactionAPI NHibernate é usada), liberar automaticamente usando a rotina explicada ou nunca liberar, a menos que Flush()seja chamado explicitamente. O último modo é útil para unidades de trabalho de execução longa, onde um ISessioné mantido aberto e desconectado por um longo período de tempo.

...

Consulte também esta seção :

O encerramento de uma sessão envolve quatro fases distintas:

  • liberar a sessão
  • confirmar a transação
  • fechar a sessão
  • lidar com exceções

Liberando a Sessão

Se você estiver usando a ITransactionAPI, não precisará se preocupar com esta etapa. Isso será realizado implicitamente quando a transação for confirmada. Caso contrário, você deve ligar ISession.Flush()para garantir que todas as alterações sejam sincronizadas com o banco de dados.

Confirmando a transação do banco de dados

Se você estiver usando a API do NHibernate ITransaction, será semelhante a:

tx.Commit(); // flush the session and commit the transaction

Se você estiver gerenciando as transações do ADO.NET, deverá manualmente Commit()a transação do ADO.NET.

sess.Flush();
currentTransaction.Commit();

Se você decidir não confirmar suas alterações:

tx.Rollback();  // rollback the transaction

ou:

currentTransaction.Rollback();

Se você reverter a transação, feche e descarte a sessão atual imediatamente para garantir que o estado interno do NHibernate seja consistente.

Fechando a ISession

Uma chamada para ISession.Close()marcar o final de uma sessão. A principal implicação de Close () é que a conexão ADO.NET será abandonada pela sessão.

tx.Commit();
sess.Close();

sess.Flush();
currentTransaction.Commit();
sess.Close();

Se você forneceu sua própria conexão, Close()retorna uma referência a ela, para que você possa fechá-la manualmente ou devolvê-la ao pool. Caso contrário, Close()ele será retornado à piscina.

Matt Hinze
fonte
2
para mim, essa linha foi a chave: "A principal implicação de Close () é que a conexão ADO.NET será abandonada pela sessão". se você não ligar para ISession.Close (), suas conexões serão preenchidas até você receber o tempo limite do banco de dados. : o
dave thieben 12/11/10
Geralmente: abre a sessão session.BeginTransaction () work ... session.Transaction.Commit () session.BeginTransaction () work ... session.Transaction.Commit () session.BeginTransaction () work .. session.Transaction.Commit () descarte a sessão.
Agile Jedi
Escrita brilhante e +1 e etc - no entanto, acho que uma edição pode ser necessária porque você diz no topo "Nunca use fechar" e depois "Se você reverter a transação, feche imediatamente e descarte a sessão atual"
SpaceBison
A ordem das instruções SQL pode ser alterada. Quero dizer, preciso executar a atualização sobre um objeto de entidade e depois inserir, porque tenho uma restrição na tabela correspondente.
precisa saber é o seguinte
14

A partir do NHibernate 2.0, as transações são necessárias para operações do banco de dados. Portanto, a ITransaction.Commit()chamada tratará de qualquer descarga necessária. Se, por algum motivo, você não estiver usando transações do NHibernate, não haverá liberação automática da sessão.

Sean Carpenter
fonte
1

Periodicamente, o ISession executará as instruções SQL necessárias para sincronizar o estado da conexão ADO.NET com o estado dos objetos mantidos na memória.

E sempre use

 using (var transaction = session.BeginTransaction())
 {
     transaction.Commit();
 }

depois que as alterações são confirmadas, essas alterações são salvas no banco de dados e usamos transaction.Commit ();

ganders
fonte
0

Aqui estão dois exemplos do meu código em que ele falharia sem session.Flush ():

http://www.lucidcoding.blogspot.co.uk/2012/05/changing-type-of-entity-persistence.html

No final, você pode ver uma seção de código em que eu ativo a inserção de identidade, salve a entidade e, em seguida, libero, e desative a inserção de identidade. Sem essa liberação, parecia estar ativando e desativando a inserção de identidade e salvando a entidade.

O uso de Flush () me deu mais controle sobre o que estava acontecendo.

Aqui está outro exemplo:

Enviando mensagem NServiceBus dentro do TransactionScope

Eu não entendo completamente o porquê, mas Flush () impediu que meu erro acontecesse.

Paul T Davies
fonte