Hibernate.orgCommunity Documentation

Capítulo 13. Transações e Concorrência

13.1. Sessão e escopos de transações
13.1.1. Unidade de trabalho
13.1.2. Longas conversações
13.1.3. Considerando a identidade do objeto
13.1.4. Edições comuns
13.2. Demarcação de transações de bancos de dados
13.2.1. Ambiente não gerenciado
13.2.2. Usando JTA
13.2.3. Tratamento de Exceção
13.2.4. Tempo de espera de Transação
13.3. Controle de concorrência otimista
13.3.1. Checagem de versão da aplicação
13.3.2. Sessão estendida e versionamento automático
13.3.3. Objetos destacados e versionamento automático
13.3.4. Versionamento automático customizado
13.4. Bloqueio Pessimista
13.5. Modos para liberar a conexão

O fator mais importante sobre o Hibernate e o controle de concorrência é que é muito fácil de ser compreendido. O Hibernate usa diretamente conexões de JDBC e recursos de JTA sem adicionar nenhum comportamento de bloqueio a mais. Recomendamos que você gaste algum tempo com o JDBC, o ANSI e a especificação de isolamento de transação de seu sistema de gerência da base de dados.

O Hibernate não bloqueia objetos na memória. Sua aplicação pode esperar o comportamento tal qual definido de acordo com o nível de isolamento de suas transações de banco de dados. Note que graças ao Session, que também é um cache de escopo de transação, o Hibernate procura repetidamente por identificadores e consultas de entidade não consultas de relatórios que retornam valores escalares.

Além do versionamento para o controle automático de concorrência otimista, o Hibernate oferece também uma API (menor) para bloqueio pessimista de linhas usando a sintáxe SELECT FOR UPDATE. O controle de concorrência otimista e esta API são discutidos mais tarde neste capítulo.

Nós começamos a discussão do controle de concorrência no Hibernate com a granularidade do Configuration, SessionFactory e Session, além de transações de base de dados e conversações longas.

Um SessionFactory é objeto threadsafe com um custo alto de criação, compartilhado por todas as threads da aplicação. É criado uma única vez, no início da execução da aplicação, a partir da instância de uma Configuration.

Uma Session é um objeto de baixo custo de criação, não é threadsafe, deve ser usado uma vez, para uma única requisição, uma conversação, uma única unidade do trabalho e então deve ser descartado. Um Session não obterá um JDBC Connection, ou um Datasource, a menos que necessite. Isto não consome nenhum recurso até ser usado.

Uma transação precisa ser o mais curta possível, para reduzir a disputa pelo bloqueio na base de dados. Transações longas impedirão que sua aplicação escale a carga altamente concorrente. Por isso, não é bom manter uma transação de base de dados aberta durante o tempo que o usuário pensa, até que a unidade do trabalho esteja completa.

Qual é o escopo de uma unidade de trabalho? Pode uma única Session do Hibernate gerenciar diversas transações ou este é um o relacionamento um-para-um dos escopos? Quando você deve abrir e fechar uma Session e como você demarca os limites da transação? Estas questões estão endereçadas nas seguintes seções.

First, let's define a unit of work. A unit of work is a design pattern described by Martin Fowler as “ [maintaining] a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. ”[PoEAA] In other words, its a series of operations we wish to carry out against the database together. Basically, it is a transaction, though fulfilling a unit of work will often span multiple physical database transactions (see Seção 13.1.2, “Longas conversações”). So really we are talking about a more abstract notion of a transaction. The term "business transaction" is also sometimes used in lieu of unit of work.

Primeiro, não use o antipattern sessão-por-operação: isto é, não abra e feche uma Session para cada simples chamada ao banco de dados em uma única thread. Naturalmente, o mesmo se aplica às transações do banco de dados. As chamadas ao banco de dados em uma aplicação são feitas usando uma seqüência planejada, elas são agrupadas em unidades de trabalho atômicas. Veja que isso também significa que realizar um auto-commit depois de cada instrução SQL é inútil em uma aplicação, esta modalidade é ideal para o trabalho ad hoc do console do SQL. O Hibernate impede, ou espera que o servidor de aplicação impessa isso, aplique a modalidade auto-commit imediatamente. As transações de banco de dados nunca são opcionais, toda a comunicação com um banco de dados tem que ocorrer dentro de uma transação, não importa se você vai ler ou escrever dados. Como explicado, o comportamento auto-commit para leitura de dados deve ser evitado, uma vez que muitas transações pequenas são improváveis de executar melhor do que uma unidade de trabalho claramente definida. A última opção é também muito mais sustentável e expandida.

O modelo mais comum em uma aplicação de cliente/servidor multi-usuário é sessão-por-requisição. Neste modelo, uma requisição do cliente é enviada ao servidor, onde a camada de persistência do Hibernate é executada. Uma Session nova do Hibernate é aberta, e todas as operações da base de dados são executadas nesta unidade do trabalho. Logo que o trabalho for completado, e a resposta para o cliente for preparada, a sessão é descarregada e fechada. Você usaria também uma única transação de base de dados para servir às requisições dos clientes, iniciando e submetendo-o ao abrir e fechar da Session. O relacionamento entre os dois é um-para-um e este modelo é um ajuste perfeito para muitas aplicações.

O desafio encontra-se na implementação. O Hibernate fornece gerenciamento integrado da "sessão atual" para simplificar este modelo. Tudo que você tem a fazer é iniciar uma transação quando uma requisição precisa ser processada e terminar a transação antes que a resposta seja enviada ao cliente. Você pode fazer onde quiser, soluções comuns são ServletFilter, interceptador AOP com um pointcut (ponto de corte) nos métodos de serviço ou em um recipiente de proxy/interceptação. Um recipiente de EJB é uma maneira padronizada de implementar aspectos cross-cutting, tais como a demarcação da transação em beans de sessão EJB, declarativamente com CMT. Se você se decidir usar demarcação programática de transação, dê preferência à API Transaction do Hibernate mostrada mais adiante neste capítulo, para facilidade no uso e portabilidade de código.

Your application code can access a "current session" to process the request by calling sessionFactory.getCurrentSession(). You will always get a Session scoped to the current database transaction. This has to be configured for either resource-local or JTA environments, see Seção 2.3, “Sessões Contextuais”.

Ás vezes, é conveniente estender o escopo de uma Session e de uma transação do banco de dados até que a "visão esteja renderizada". É especialmente útil em aplicações servlet que utilizam uma fase de renderização separada depois da requisição ter sido processada. Estender a transação até que a renderização da visão esteja completa é fácil de fazer se você implementar seu próprio interceptador. Entretanto, não será fácil se você confiar em EJBs com transações gerenciadas por recipiente, porque uma transação será terminada quando um método de EJB retornar, antes que a renderização de toda visão possa começar. Veja o website e o fórum do Hibernate para dicas e exemplos em torno deste modelo de Sessão Aberta na Visualização.

O modelo sessão-por-requisição não é o único conceito útil que você pode usar ao projetar unidades de trabalho. Muitos processos de negócio requerem uma totalidade de séries de interações com o usuário, intercaladas com acessos a uma base de dados. Em aplicações da web e corporativas não é aceitável que uma transação atrapalhe uma interação do usuário. Considere o seguinte exemplo:

Nós chamamos esta unidade de trabalho, do ponto da visão do usuário, uma conversação de longa duração (ou transação da aplicação). Há muitas maneiras de você implementar em sua aplicação.

Uma primeira implementação simples pode manter a Session e a transação aberta durante o tempo de interação do usuário, com bloqueios na base de dados para impedir a modificação concorrente e para garantir o isolamento e a atomicidade. Esse é naturalmente um anti-pattern, uma vez que a disputa do bloqueio não permitiria o escalonameneto da aplicação com o número de usuários concorrentes.

Claramente, temos que usar diversas transações para implementar a conversação. Neste caso, manter o isolamento dos processos de negócio, torna-se responsabilidade parcial da camada da aplicação. Uma única conversação geralmente usa diversas transações. Ela será atômica se somente uma destas transações (a última) armazenar os dados atualizados, todas as outras simplesmente leram os dados (por exemplo em um diálogo do estilo wizard que mede diversos ciclos de requisição/resposta). Isto é mais fácil de implementar do parece, especialmente se você usar as características do Hibernate:

Tanto a sessão-por-solicitação-com-objetos-desanexados quanto a sessão-por-conversação possuem vantagens e desvantagens. Estas desvantagens serão discutidas mais tarde neste capítulo no contexto do controle de concorrência otimista.

Uma aplicação pode acessar concorrentemente o mesmo estado persistente em duas Sessions diferentes. Entretanto, uma instância de uma classe persistente nunca é compartilhada entre duas instâncias Session. Portanto, há duas noções diferentes da identidade:

Então para os objetos acoplados a uma Session específica (ex.: isto está no escopo de uma Session), as duas noções são equivalentes e a identidade da JVM para a identidade da base de dados é garantida pelo Hibernate. Entretanto, embora a aplicação possa acessar concorrentemente o "mesmo" objeto do negócio (identidade persistente) em duas sessões diferentes, as duas instâncias serão realmente "diferentes" (identidade de JVM). Os conflitos são resolvidos usando (versionamento automático) no flush/commit, usando uma abordagem otimista.

Este caminho deixa o Hibernate e o banco de dados se preocuparem com a concorrência. Ele também fornece uma escalabilidade melhor, garantindo que a identidade em unidades de trabalho single-threaded não necessite de bloqueio dispendioso ou de outros meios de sincronização. A aplicação nunca necessita sincronizar qualquer objeto de negócio tão longo que transpasse uma única thread por Session. Dentro de uma Session a aplicação pode usar com segurança o == para comparar objetos.

No entanto, uma aplicação que usa == fora de uma Session, pode ver resultados inesperados. Isto pode ocorrer mesmo em alguns lugares inesperados, por exemplo, se você colocar duas instâncias desacopladas em um mesmo Set. Ambas podem ter a mesma identidade na base de dados (ex.: elas representam a mesma linha), mas a identidade da JVM não é, por definição, garantida para instâncias em estado desacoplado. O desenvolvedor tem que substituir os métodos equals() e hashCode() em classes persistentes e implementar sua própria noção da igualdade do objeto. Advertência: nunca use o identificador da base de dados para implementar a igualdade, use atributos de negócio, uma combinação única, geralmente imutável. O identificador da base de dados mudará se um objeto transiente passar para o estado persistente. Se a instância transiente (geralmente junto com instâncias desacopladas) for inserida em um Set, a mudança do hashcode quebrará o contrato do Set. As funções para chaves de negócio não têm que ser tão estável quanto às chaves primárias da base de dados, você somente tem que garantir a estabilidade durante o tempo que os objetos estiverem no mesmo Set. Veja o website do Hibernate para uma discussão mais completa sobre o assunto. Note também que esta não é uma característica do Hibernate, mas simplesmente a maneira como a identidade e a igualdade do objeto de Java têm que ser implementadas.

Nunca use o anti-patterns sessão-por-usuário-sessão ou sessão-por-aplicação (naturalmente, existem exceções raras para essa regra). Note que algumas das seguintes edições podem também aparecer com modelos recomendados, certifique-se que tenha compreendido as implicações antes de fazer uma decisão de projeto:

Os limites de uma transação de banco de dados, ou sistema, são sempre necessários. Nenhuma comunicação com o banco de dados pode ocorrer fora de uma transação de banco de dados (isto parece confundir muitos desenvolvedores que estão acostumados ao modo auto-commit). Sempre use os limites desobstruídos da transação, até mesmo para operações somente leitura. Dependendo de seu nível de isolamento e capacidade da base de dados isto pode não ser requerido, mas não há nenhum aspecto negativo se você sempre demarcar transações explicitamente. Certamente, uma única transação será melhor executada do que muitas transações pequenas, até mesmo para dados de leitura.

Uma aplicação do Hibernate pode funcionar em ambientes não gerenciados (isto é, aplicações standalone, Web simples ou Swing) e ambientes gerenciados J2EE. Em um ambiente não gerenciado, o Hibernate é geralmente responsável pelo seu próprio pool de conexões. O desenvolvedor, precisa ajustar manualmente os limites das transaçãos, ou seja, começar, submeter ou efetar rollback nas transações ele mesmo. Um ambiente gerenciado fornece transações gerenciadas por recipiente (CMT), com um conjunto da transações definido declarativamente em descritores de implementação de beans de sessão EJB, por exemplo. A demarcação programática é portanto, não mais necessária.

Entretanto, é freqüentemente desejável manter sua camada de persistência portável entre ambientes de recurso locais não gerenciados e sistemas que podem confiar em JTA, mas use BMT ao invés de CMT. Em ambos os casos você usaria demarcação de transação programática. O Hibernate oferece uma API chamada Transaction que traduz dentro do sistema de transação nativa de seu ambiente de implementação. Esta API é realmente opcional, mas nós encorajamos fortemente seu uso a menos que você esteja em um bean de sessão CMT.

Geralmente, finalizar uma Session envolve quatro fases distintas:

A liberação da sessão já foi bem discutida, agora nós daremos uma olhada na demarcação da transação e na manipulação de exceção em ambientes controlados e não controlados.

Se uma camada de persistência do Hibernate roda em um ambiente não gerenciado, as conexões do banco de dados são geralmente tratadas pelos pools de conexões simples (ex.: não DataSource) dos quais o Hibernate obtém as conexões assim que necessitar. A maneira de se manipular uma sessão/transação é mais ou menos assim:

// Non-managed environment idiom

Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();
    // do some work
    ...
    tx.commit();
}
catch (RuntimeException e) {
    if (tx != null) tx.rollback();
    throw e; // or display error message
}
finally {
    sess.close();
}

Você não pode chamar flush() da Session() explicitamente. A chamada ao commit() dispara automaticamente a sincronização para a sessão, dependendo do Seção 11.10, “Limpando a Sessão”. Uma chamada ao close() marca o fim de uma sessão. A principal implicação do close() é que a conexão JDBC será abandonada pela sessão. Este código Java é portável e funciona em ambientes não gerenciados e de JTA.

Uma solução muito mais flexível é o gerenciamento de contexto "sessão atual" da construção interna do Hibernate, como descrito anteriormente:

// Non-managed environment idiom with getCurrentSession()

try {
    factory.getCurrentSession().beginTransaction();
    // do some work
    ...
    factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
    factory.getCurrentSession().getTransaction().rollback();
    throw e; // or display error message
}

Você muito provavelmente nunca verá estes fragmentos de código em uma aplicação regular; as exceções fatais (do sistema) devem sempre ser pegas no "topo". Ou seja, o código que executa chamadas do Hibernate (na camada de persistência) e o código que trata RuntimeException (e geralmente pode somente limpar acima e na saída) estão em camadas diferentes. O gerenciamento do contexto atual feito pelo Hibernate pode significativamente simplificar este projeto, como tudo que você necessita é do acesso a um SessionFactory. A manipulação de exceção é discutida mais tarde neste capítulo.

Note que você deve selecionar org.hibernate.transaction.JDBCTransactionFactory, que é o padrão, e para o segundo exemplo "thread" como seu hibernate.current_session_context_class.

Se sua camada de persistência funcionar em um servidor de aplicação (por exemplo, dentro dos beans de sessão EJB), cada conexão da fonte de dados obtida pelo Hibernate automaticamente fará parte da transação global de JTA. Você pode também instalar uma implementação standalone de JTA e usá-la sem EJB. O Hibernate oferece duas estratégias para a integração de JTA.

Se você usar transações de bean gerenciado (BMT) o Hibernate dirá ao servidor de aplicação para começar e para terminar uma transação de BMT se você usar a Transaction API. Assim, o código de gerência de transação é idêntico ao ambiente não gerenciado.

// BMT idiom

Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();
    // do some work
    ...
    tx.commit();
}
catch (RuntimeException e) {
    if (tx != null) tx.rollback();
    throw e; // or display error message
}
finally {
    sess.close();
}

Se você quiser usar uma Session limitada por transação, isto é, a funcionalidade do getCurrentSession() para a propagação fácil do contexto, você terá que usar diretamente a API JTA UserTransaction:

// BMT idiom with getCurrentSession()

try {
    UserTransaction tx = (UserTransaction)new InitialContext()
                            .lookup("java:comp/UserTransaction");
    tx.begin();
    // Do some work on Session bound to transaction
    factory.getCurrentSession().load(...);
    factory.getCurrentSession().persist(...);
    tx.commit();
}
catch (RuntimeException e) {
    tx.rollback();
    throw e; // or display error message
}

Com CMT, a demarcação da transação é feita em descritores de implementação de beans de sessão, não programaticamente, conseqüentemente, o código é reduzido a:

// CMT idiom

 Session sess = factory.getCurrentSession();
 // do some work
 ...

Em um CMT/EJB, até mesmo um rollback acontece automaticamente, desde que uma exceção RuntimeException não tratável seja lançada por um método de um bean de sessão que informa ao recipiente ajustar a transação global ao rollback. Isto significa que você não precisa mesmo usar a API Transaction do Hibernate com BMT ou CMT e você obterá a propagação automática da Sessão "atual" limitada à transação.

Veja que você deverá escolher org.hibernate.transaction.JTATransactionFactory se você usar o JTA diretamente (BMT) e org.hibernate.transaction.CMTTransactionFactory em um bean de sessão CMT, quando você configura a fábrica de transação do Hibernate. Lembre-se também de configurar o hibernate.transaction.manager_lookup_class. Além disso, certifique-se que seu hibernate.current_session_context_class ou não é configurado (compatibilidade com o legado) ou está definido para "jta".

A operação getCurrentSession() tem um aspecto negativo em um ambiente JTA. Há uma advertência para o uso do método liberado de conexão after_statement, o qual é usado então por padrão. Devido a uma limitação simples da especificação JTA, não é possível para o Hibernate automaticamente limpar quaisquer instâncias ScrollableResults ou Iterator abertas retornadas pelo scroll() ou iterate(). Você deve liberar o cursor subjacente da base de dados chamando ScrollableResults.close() ou Hibernate.close(Iterator) explicitamente de um bloco finally. Claro que a maioria das aplicações podem facilmente evitar o uso do scroll() ou do iterate() em todo código provindo do JTA ou do CMT.

Se a Session levantar uma exceção, incluindo qualquer SQLException, você deverá imediatamente dar um rollback na transação do banco, chamando Session.close() e descartando a instância da Session. Certos métodos da Sessionnão deixarão a sessão em um estado inconsistente. Nenhuma exceção lançada pelo Hibernate pode ser recuperada. Certifique-se que a Session será fechada chamando close() no bloco finally.

A exceção HibernateException, a qual envolve a maioria dos erros que podem ocorrer em uma camada de persistência do Hibernate, é uma exceção não verificada. Ela não constava em versões mais antigas de Hibernate. Em nossa opinião, nós não devemos forçar o desenvolvedor a tratar uma exceção irrecuperável em uma camada mais baixa. Na maioria dos sistemas, as exceções não verificadas e fatais são tratadas em um dos primeiros frames da pilha da chamada do método (isto é, em umas camadas mais elevadas) e uma mensagem de erro é apresentada ao usuário da aplicação (ou alguma outra ação apropriada é feita). Note que Hibernate pode também lançar outras exceções não verificadas que não sejam um HibernateException. Estas, também são, irrecuperáveis e uma ação apropriada deve ser tomada.

O Hibernate envolve SQLExceptions lançadas ao interagir com a base de dados em um JDBCException. Na realidade, o Hibernate tentará converter a exceção em uma subclasse mais significativa da JDBCException. A SQLException subjacente está sempre disponível através de JDBCException.getCause(). O Hibernate converte a SQLException em uma subclasse JDBCException apropriada usando SQLExceptionConverter associado ao SessionFactory. Por padrão, o SQLExceptionConverter é definido pelo dialeto configurado. Entretanto, é também possível conectar em uma implementação customizada. Veja o javadoc para mais detalhes da classe SQLExceptionConverterFactory. Os subtipos padrão de JDBCException são:

O único caminho que é consistente com a elevada concorrência e escalabilidade é o controle de concorrência otimista com versionamento. A checagem de versão usa número de versão, ou carimbo de hora (timestamp), para detectar conflitos de atualizações (e para impedir atualizações perdidas). O Hibernate fornece três caminhos possíveis para escrever aplicações que usam concorrência otimista. Os casos de uso que nós mostramos estão no contexto de conversações longas, mas a checagem de versão também tem o benefício de impedir atualizações perdidas em únicas transações.

Em uma implementação sem muita ajuda do Hibernate, cada interação com o banco de dados ocorre em uma nova Session e o desenvolvedor é responsável por recarregar todas as instâncias persistentes da base de dados antes de manipulá-las. Este caminho força a aplicação a realizar sua própria checagem de versão para assegurar a conversação do isolamento da transação. Este caminho é menos eficiente em termos de acesso ao banco de dados. É o caminho mais similar à entidade EJBs.

// foo is an instance loaded by a previous Session

session = factory.openSession();
Transaction t = session.beginTransaction();
int oldVersion = foo.getVersion();
session.load( foo, foo.getKey() ); // load the current state
if ( oldVersion != foo.getVersion() ) throw new StaleObjectStateException();
foo.setProperty("bar");
t.commit();
session.close();

A propriedade version é mapeada usando <version>, e o Hibernate vai incrementá-lá automaticamente durante a liberação se a entidade estiver alterada.

Claro, se você estiver operando em um ambiente de baixa concorrência de dados e não precisar da checagem de versão, você pode usar este caminho e apenas pular a checagem de versão. Nesse caso, o último commit realizado é a estratégia padrão para suas conversações longas. Tenha em mente que isto pode confundir os usuários da aplicação, como também poderão ter atualizações perdidas sem mensagens de erro ou uma possibilidade de ajustar mudanças conflitantes.

Claro que, a checagem manual da versão é somente possível em circunstâncias triviais e não para a maioria de aplicações. Freqüentemente, os gráficoscompletos de objetos modificados têm que ser verificados, não somente únicas instâncias. O Hibernate oferece checagem de versão automática com uma Session estendida ou instâncias desatachadas como o paradigma do projeto.

Uma única instância de Session e suas instâncias persistentes são usadas para a conversação inteira, isto é conhecido como sessão-por-conversação. O Hibernate verifica versões da instância no momento da liberação, lançando uma exceção se a modificação concorrente for detectada. Até o desenvolvedor pegar e tratar essa exceção. As opções comuns são a oportunidade para que o usuário intercale as mudanças ou reinicie a conversação do negócio com dados não antigos.

A Session é desconectada de toda a conexão JDBC adjacente enquanto espera a interação do usuário. Este caminho é o mais eficiente em termos de acesso a bancos de dados. A aplicação não precisa se preocupar com a checagem de versão ou com as instâncias destacadas reatadas, nem precisa recarregar instâncias a cada transação.

// foo is an instance loaded earlier by the old session

Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction
foo.setProperty("bar");
session.flush();    // Only for last transaction in conversation
t.commit();         // Also return JDBC connection
session.close();    // Only for last transaction in conversation

O objeto foo sabe que a Session já foi carregada. Ao começar uma nova transação ou uma sessão velha, você obterá uma conexão nova e reiniciará a sessão. Submeter uma transação implica em desconectar uma sessão da conexão JDBC e retornar à conexão ao pool. Após a reconexão, para forçar uma checagem de versão em dados que você não esteja atualizando, você poderá chamar Session.lock() com o LockMode.READ em todos os objetos que possam ter sido atualizados por uma outra transação. Você não precisa bloquear nenhum dado que você está atualizando. Geralmente, você configuraria FlushMode.NEVER em uma Session estendida, de modo que somente o último ciclo da transação tenha permissão de persistir todas as modificações feitas nesta conversação. Por isso, somente esta última transação incluiria a operação flush() e então também iria close() a sessão para terminar a conversação.

Este modelo é problemático se a Session for demasiadamente grande para ser armazenada durante o tempo de espera do usuário (por exemplo uma HttpSession deve ser mantida o menor possível). Como a Session é também cache de primeiro nível (imperativo) e contém todos os objetos carregados, nós podemos provavelmente usar esta estratégia somente para alguns ciclos de requisição/resposta. Você deve usar a Session somente para uma única conversação, porque ela logo também estará com dados velhos.

Note também que você deve manter a Session desconectada, fechada para a camada de persistência. Ou seja, use um bean de sessão com estado EJB para prender a Session em um ambiente de três camadas. Não transfira à camada web, ou até serializá-lo para uma camada separada, para armazená-lo no HttpSession.

O modelo da sessão estendida, ou sessão-por-conversação, é mais difícil de implementar com gerenciamento automático de sessão atual. Você precisa fornecer sua própria implementação do CurrentSessionContext para isto. Veja o Hibernate Wiki para exemplos.

Você pode desabilitar o incremento da versão automática de Hibernate para propriedades e coleções particulares, configurando a função de mapeamento optimistic-lock para false. O Hibernate então, não incrementará mais versões se a propriedade estiver modificada.

Os esquemas da base de dados legado são freqüentemente estáticos e não podem ser modificados. Ou então, outras aplicações puderam também acessar a mesma base de dados e não sabem tratar a versão dos números ou carimbos de hora. Em ambos os casos, o versionamento não pode confiar em uma coluna particular em uma tabela. Para forçar uma checagem de versão sem uma versão ou mapeamento da propriedade do carimbo de hora com uma comparação do estado de todos os campos em uma linha, configure optimistic-lock="all" no mapeamento <class>. Note que isto conceitualmente é somente feito em trabalhos se o Hibernate puder comparar o estado velho e novo (ex.: se você usar uma única Session longa e não uma sessão-por-solicitação-com-objetos-desanexados).

Às vezes a modificação concorrente pode ser permitida, desde que as mudanças realizadas não se sobreponham. Se você configurar optimistic-lock="dirty" ao mapear o <class>, o Hibernate comparará somente campos modificados durante a liberação.

Em ambos os casos, com as colunas de versão/carimbo de hora dedicados com comparação de campo cheio/sujo, o Hibernate usa uma única instrução UPDATE, com uma cláusula WHERE apropriada, por entidade para executar a checagem da versão e atualizar a informação. Se você usar a persistência transitiva para cascatear o reatamento das entidades associadas, o Hibernate pode executar atualizações desnecessárias. Isso não é geralmente um problema, mas os triggers em atualizações num banco de dados pode ser executado mesmo quando nenhuma mudança foi feita nas instâncias desanexadas. Você pode customizar este comportamento configurando selecionar-antes-de atualizar="verdadeiro" no mapeamento <class>, forçando o Hibernate a fazer um SELECT nas instâncias para assegurar-se de que as mudanças realmente aconteceram, antes de atualizar a linha.

Não ha intenção alguma que usuários gastem muitas horas se preocupando com suas estratégias de bloqueio. Geralmente, é o bastante especificar um nível de isolamento para as conexões JDBC e então deixar simplesmente o banco de dados fazer todo o trabalho. Entretanto, os usuários avançados podem às vezes desejar obter bloqueios pessimistas exclusivos, ou re-obter bloqueios no início de uma nova transação.

O Hibernate usará sempre o mecanismo de bloqueio da base de dados, nunca bloquiar objetos na memória.

A classe LockMode define os diferentes níveis de bloqueio que o Hibernate pode adquirir. Um bloqueio é obtido pelos seguintes mecanismos:

O bloqueio obtido "explicitamente pelo usuário" se dá nas seguintes formas:

Se uma Session.load() é invocada com UPGRADE ou UPGRADE_NOWAIT, e o objeto requisitado ainda não foi carregado pela sessão, o objeto é carregado usando SELECT ... FOR UPDATE. Se load() for chamado para um objeto que já foi carregado com um bloqueio menos restritivo que o novo bloqueio solicitado, o Hibernate invoca o método lock() para aquele objeto.

O Session.lock() executa uma verificação no número da versão se o modo de bloqueio especificado for READ, UPGRADE ou UPGRADE_NOWAIT. No caso do UPGRADE ou UPGRADE_NOWAIT, é usado SELECT ... FOR UPDATE.

Se o banco de dados não suportar o modo de bloqueio solicitado, o Hibernate usará um modo alternativo apropriado, ao invés de lançar uma exceção. Isso garante que a aplicação seja portátil.

O comportamento legado do Hibernate 2.x referente ao gerenciamento da conexão via JDBC era que a Session precisaria obter uma conexão quando ela precisasse pela primeira vez e depois manteria a conexão enquanto a sessão não fosse fechada. O Hibernate 3.x introduz a idéia de modos para liberar a sessão, para informar a sessão a forma como deve manusear a sua conexão JDBC. Veja que essa discussão só é pertinente para conexões fornecidas com um ConnectionProvider configurado. As conexões fornecidas pelo usuário estão fora do escopo dessa discussão. Os diferentes modos de liberação estão definidos pelos valores da enumeração org.hibernate.ConnectionReleaseMode:

O parâmetro de configuração hibernate.connection.release_mode é usado para especificar qual modo de liberação deve ser usado. Segue abaixo os valores possíveis: