Por que eu preciso do Transaction no Hibernate para operações somente leitura?

106

Por que eu preciso do Transaction no Hibernate para operações somente leitura?

A transação a seguir bloqueia o BD?

Código de exemplo para buscar no banco de dados:

Transaction tx = HibernateUtil.getCurrentSession().beginTransaction(); // why begin transaction?
//readonly operation here

tx.commit() // why tx.commit? I don't want to write anything

Posso usar em session.close() vez de tx.commit()?

user93796
fonte
9
A transação é exigida pelo próprio banco de dados. Você pode ler sobre o modo autocommit aqui: community.jboss.org/wiki/…
Bhesh Gurung
@BheshGurung eu acho que exigimos a transcação apenas para a operação de gravação
user93796
4
Você leu a parte "Desmascarando os mitos do auto-commit" no link?
Bhesh Gurung

Respostas:

131

Você pode realmente ter motivos para marcar as transações como somente leitura.

  1. As transações para leitura podem parecer realmente estranhas e muitas vezes as pessoas não marcam métodos para transações neste caso. Mas o JDBC criará a transação de qualquer maneira, apenas funcionará autocommit=truese uma opção diferente não for definida explicitamente.
  2. Mas não há garantia de que seu método não grava no banco de dados. Se você marcar o método como @Transactional(readonly=true), o Spring definirá a transação JDBC em um modo somente leitura, portanto, você ditará se é realmente possível gravar no banco de dados no escopo desta transação. Se sua arquitetura é complicada e alguns membros da equipe podem optar por colocar a consulta de modificação onde não é esperada, este sinalizador apontaria para o local problemático.
  3. Além disso, as transações somente leitura podem ser otimizadas por bancos de dados, mas é claro que isso é específico do banco de dados. Por exemplo, o MySQL adicionou suporte para isso apenas no InnoDB a partir da versão 5.6.4.
  4. Se você não estiver usando JDBC diretamente, mas um ORM, isso pode ser problemático. Por exemplo, a comunidade do Hibernate diz que trabalhar fora da transação pode causar um comportamento imprevisível. Isso ocorre porque o Hibernate abrirá a transação, mas não a fechará por conta própria, portanto a conexão será retornada ao Pool de Conexões com a transação não sendo confirmada. O que acontece depois? O JDBC mantém silêncio, portanto, esta é uma implementação específica (o MySQL reverte a transação, o caso do Oracle a confirma). Isso também pode ser configurado no nível do pool de conexão (por exemplo, C3P0 oferece essa opção, reversão por padrão).
  5. Outra coisa quando se trata de Hibernate, o Spring define o FlushMode para MANUAL no caso de transações somente leitura, o que leva a outras otimizações, sem a necessidade de verificações sujas.
  6. Você pode desejar substituir ou definir explicitamente o nível de isolamento da transação. Isso também afeta as transações de leitura, pois você quer ou não ler alterações não confirmadas, ser exposto a leituras fantasmas, etc.

Resumindo - você pode seguir os dois caminhos, mas precisa entender as consequências.

Stanislav Bashkyrtsev
fonte
Obrigado por responder. Estou usando o hibernate (hibernate nativo, não jpa) .Mas o hibernate me força a iniciar uma transcação antes de fazer qualquer operação de banco de dados. , se sim como?
user93796
O sinalizador readonly é definido para conexão, não para transação em si, e você não pode acessá-lo via Hibernate dessa forma. Você precisa usar o suporte Spring Transaction e usar anotações ou configuração transacional baseada em XML. Fazendo isso, o Spring assume a propriedade do gerenciamento de conexão e transação ao invés do Hibernate.
Stanislav Bashkyrtsev
1
Muito bem explicado! Outro artigo de Mark Richards explica muito bem as armadilhas da sinalização somente leitura na anotação Transactional - ibm.com/developerworks/java/library/j-ts1/index.html .
Mahesh
47

Todas as instruções do banco de dados são executadas no contexto de uma transação física, mesmo quando não declaramos explicitamente os limites da transação (BEGIN / COMMIT / ROLLBACK).

Se você não declarar os limites da transação explicitamente, cada instrução terá que ser executada em uma transação separada ( autocommitmodo). Isso pode até mesmo levar à abertura e fechamento de uma conexão por instrução, a menos que seu ambiente possa lidar com vinculação de conexão por thread.

Declarar um serviço como @Transactionalfornecerá uma conexão para toda a duração da transação e todas as instruções usarão essa única conexão de isolamento. Isso é muito melhor do que não usar transações explícitas em primeiro lugar.

Em aplicativos grandes, você pode ter muitas solicitações simultâneas e reduzir a taxa de solicitação de aquisição de conexão com o banco de dados definitivamente melhorará o desempenho geral do aplicativo.

JPA não impõe transações em operações de leitura. Apenas as gravações acabam lançando uma exceção de transação necessária caso você se esqueça de iniciar um contexto transacional. No entanto, é sempre melhor declarar os limites da transação, mesmo para transações somente leitura (no Spring @Transactionalpermite marcar transações somente leitura, o que tem um grande benefício de desempenho).

Vlad Mihalcea
fonte
1
"... então cada instrução terá que ser executada em uma transação separada". O contêiner não faz o processamento em lote automaticamente?
Arash
O processamento em lote é para modificações, não para leitura de dados. E mesmo assim, o batching JDBC não é habilitado por padrão ao usar JPA e Hibernate.
Vlad Mihalcea
1
Obrigado Vlad. Eu li alguns de seus artigos. Eles são realmente úteis.
Arash
Este stackoverflow.com/q/34797480/179850 parece contradizer um pouco sua afirmação de que transações somente leitura têm grande benefício de desempenho. Pelo menos, não parece ser o caso no contexto de hibernação.
nimai
Não, não importa. Trata-se de definir somente leitura, enquanto o meu é sobre como evitar auto-commit.
Vlad Mihalcea
17

As transações, de fato, colocam bloqueios no banco de dados - bons mecanismos de banco de dados lidam com bloqueios simultâneos de maneira sensata - e são úteis com uso somente leitura para garantir que nenhuma outra transação adicione dados que tornem sua visualização inconsistente. Você sempre deseja uma transação (embora às vezes seja razoável ajustar o nível de isolamento, é melhor não fazer isso no início); se você nunca grava no banco de dados durante a transação, tanto o commit quanto o rollback da transação serão iguais (e muito baratos).

Agora, se você tiver sorte e suas consultas no banco de dados forem tais que o ORM sempre as mapeia para consultas SQL únicas, você pode escapar sem transações explícitas , contando com o comportamento de autocommit integrado do banco de dados, mas os ORMs são sistemas relativamente complexos portanto, não é nada seguro confiar em tal comportamento, a menos que você trabalhe muito mais para verificar o que a implementação realmente faz. Escrever os limites explícitos da transação é muito mais fácil de acertar (especialmente se você pode fazer isso com AOP ou alguma técnica orientada por ORM semelhante; do Java 7 em diante, tente com recursos também pode ser usado, suponho).

Donal Fellows
fonte
14

Não importa se você apenas lê ou não - o banco de dados ainda deve manter o controle de seu conjunto de resultados, porque outros clientes de banco de dados podem querer gravar dados que alterariam seu conjunto de resultados.

Tenho visto programas com falha para matar sistemas de banco de dados enormes, porque eles apenas leem os dados, mas nunca confirmam, forçando o log de transações a crescer, porque o banco de dados não pode liberar os dados da transação antes de um COMMIT ou ROLLBACK, mesmo se o cliente não fez nada por horas.

Ingo
fonte