Hibernate e Lock de Banco de Dados 1. Por que usar bloqueios?
Para descobrir por que o mecanismo de bloqueio existe, você deve primeiro entender o conceito de transações.
Uma transação é uma série de operações relacionadas em um banco de dados e deve ter características ácidas:
Nosso banco de dados relacional comumente usado RDBMS implementa essas características das transações. Entre eles, atomicidade,
A consistência e a persistência são garantidas pela extração de log. O isolamento é alcançado pelo mecanismo de travamento com o qual estamos preocupados hoje, e é por isso que precisamos do mecanismo de travamento.
Se não houver trava nem controle sobre o isolamento, que consequências podem ser causadas?
Vamos dar uma olhada no exemplo de hibernação. Dois threads iniciam duas operações de transação, respectivamente. A mesma linha de dados na tabela TB_ACCOUNT é col_id = 1.
pacote com.cdai.orm.hibernate.annotation; importar java.io.serializable; importar javax.persistence.column; importar javax.persistence.Entity; importar javax.persistence.id; importar javax.persistence.table; @Entity @Table (name = "tb_account") Conta da classe pública implementa serializável {private estático final serialversionuid = 5018821760412231859l; @Id @column (name = "col_id") Private Long Id; @Column (name = "col_balance") Private Long Balance; Public Account () {} Public Conta (Long Id, Longo Balanço) {this.id = id; this.Balance = Balance; } public long getId () {return id; } public void setId (longo id) {this.id = id; } public Long GetBalance () {Return Balance; } public void SetBalance (Longo Balance) {this.Balance = Balance; } @Override public string tostring () {return "conta [id =" + id + ", balance =" + balance + "]"; }} pacote com.cdai.orm.hibernate.transaction; importar org.hibernate.session; importar org.Hibernate.SessionFactory; importar org.hibernate.transaction; importar org.hibernate.cfg.annotationConfiguration; importar com.cdai.orm.hibernate.annotation.account; classe pública DirtyRead {public static void main (string [] args) {final sessionFactory SessionFactory = new AnoTationConfiguration (). addfile ("hibernate/hibernate.cfg.xml"). configure (). addPackage ("com.cdai.orm.hibernate.annotation"). addannotatedclass (conta.class). buildSessionFactory (); Thread t1 = new Thread () {@Override public void run () {Session Session1 = sessionFactory.opensession (); Transação tx1 = null; tente {tx1 = session1.begIntransaction (); System.out.println ("T1 - BEGIN Trasaction"); Thread.sleep (500); Conta de conta = (conta) session1.get (conta.class, novo long (1)); System.out.println ("t1 - balance =" + conta.getBalance ()); Thread.sleep (500); conta.setBalance (conta.getBalance () + 100); System.out.println ("T1 - Alterar o equilíbrio:" + conta.getBalance ()); tx1.Commit (); System.out.println ("T1 - Committ Transaction"); Thread.sleep (500); } catch (Exceção e) {e.printStackTrace (); if (tx1! = null) tx1.rollback (); } finalmente {session1.close (); }}}}; // 3.Run Transaction 2 Thread T2 = new Thread () {@Override public void run () {session2 = sessionFactory.opensssion (); Transação tx2 = nulo; tente {tx2 = session2.begIntransaction (); System.out.println ("T2 - BEGIN Trasaction"); Thread.sleep (500); Conta de conta = (conta) session2.get (conta.class, novo long (1)); System.out.println ("t2 - balance =" + conta.getBalance ()); Thread.sleep (500); conta.setBalance (conta.getBalance () - 100); System.out.println ("T2 - Alterar o equilíbrio:" + conta.getBalance ()); tx2.Commit (); System.out.println ("T2 - Committ Transaction"); Thread.sleep (500); } catch (Exceção e) {e.printStackTrace (); if (tx2! = null) tx2.rollback (); } finalmente {session2.close (); }}}}; t1.start (); t2.start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } catch (interruptedException e) {}} System.out.println ("T1 e T2 estão mortos."); sessionFactory.close (); }} A transação 1 reduz o Col_Balance em 100, enquanto a transação 2 a reduz em 100, o resultado final pode ser 0 ou 200 e a atualização da transação 1 ou 2 pode ser perdida. A saída de log também confirma isso, transações 1 e 2
Log Cross-Print.
T1 - BEGIN TRASACIONCT2 - BEGIN TRASACIONHIBERNATE: SELECT RECCLATS_.COL_ID AS COL1_0_0_, CONSCRUÇÃO0_.COL_BALANCE COM COL2_0_0_ DE TB_ACCOUNTENTS0_ WHERE COCCT0_.COL_ID =? tb_account conta0_ onde conta0_.col_id =? onde col_id =? Hibernate: atualize tb_account Set col_balance =? onde col_id =? T1 - Compromer transactionT2 - Commitem transação Both T1 e T2 estão mortos.
Pode -se ver que o isolamento é uma questão que precisa de consideração cuidadosa e é necessário entender os bloqueios.
2. Quantos tipos de bloqueios existem?
Os comuns são bloqueios compartilhados, bloqueios de atualização e bloqueios exclusivos.
1. Bloqueio compartilhado: usado para ler operações de dados, permitindo que outras transações sejam lidas simultaneamente. Quando uma transação executa uma instrução SELECT,
O banco de dados atribui automaticamente um bloqueio compartilhado à transação para bloquear os dados de leitura.
2. Bloqueio exclusivo: usado para modificar dados, outras transações não podem ser lidas ou modificadas. Quando a transação executa inserir,
Quando a atualização e exclusão são atualizadas, o banco de dados será alocado automaticamente.
3. Bloqueio de atualização: Usado para evitar impulsos causados por bloqueios compartilhados durante as operações de atualização, como as transações 1 e 2 segurando bloqueios compartilhados ao mesmo tempo e aguardando para obter bloqueios exclusivos. Ao executar a atualização, a transação primeiro adquire o bloqueio de atualização e depois atualiza o bloqueio de atualização para um bloqueio exclusivo, evitando o impasse.
Além disso, todos esses bloqueios podem ser aplicados a diferentes objetos no banco de dados, ou seja, esses bloqueios podem ter granularidades diferentes.
Como bloqueios no nível do banco de dados, bloqueios no nível da mesa, bloqueios no nível da página, bloqueios no nível da chave e bloqueios no nível da linha.
Portanto, existem muitos tipos de bloqueios. É muito difícil dominar completamente e usar tantas bloqueios de maneira flexível. Nós não somos DBAs.
O que fazer? Felizmente, o mecanismo de bloqueio é transparente para usuários comuns. O banco de dados adicionará automaticamente os bloqueios apropriados e atualizará e rebaixará automaticamente vários bloqueios no momento certo. É tão atencioso! Tudo o que precisamos fazer é aprender a definir o nível de isolamento de acordo com diferentes necessidades de negócios.
3. Como definir o nível de isolamento?
De um modo geral, o sistema de banco de dados fornece quatro níveis de isolamento de transações para os usuários escolherem:
1.Serializable: Quando duas transações manipulam os mesmos dados ao mesmo tempo, a transação 2 só pode parar e esperar.
2. Leitura reembolsável (repetível): a transação 1 pode ver os dados recém -inseridos da transação 2 e não pode ver as atualizações dos dados existentes.
3.rereia comprometida (leitura dados comprometidos): a transação 1 pode ver dados recém -inseridos e atualizados da transação 2.
4.rereira não comprometida (Leia os dados não comprometidos): a transação 1 pode ver a inserção e atualizar dados que a transação 2 não se comprometeu.
4. Bloqueios no aplicativo
Quando o banco de dados adota o nível de isolamento da Comissão de Read, bloqueios pessimistas ou bloqueios otimistas podem ser usados no aplicativo.
1. Bloqueio pessimista: Suponha que os dados da operação de transação atual terão definitivamente outro acesso à transação; portanto, especifique pessimisticamente que o bloqueio exclusivo é usado no aplicativo para bloquear os recursos de dados. Apoie as seguintes formas em MySQL e Oracle:
Selecione ... para atualização
Deixe explicitamente que se selecione Usar bloqueio exclusivo para bloquear os registros da consulta. Para outras transações para consultar, atualizar ou excluir esses dados bloqueados, eles devem esperar até que a transação termine.
No Hibernate, você pode passar no LockMode.Upgrade ao carregar para adotar o bloqueio pessimista. Modificar o exemplo anterior,
Nas chamadas de método get das transações 1 e 2, um parâmetro adicional de bloqueio é passado. Como pode ser visto no log, as transações 1 e 2
Não é mais a execução cruzada, a transação 2 pode aguardar o fim da transação 1 antes de ler os dados; portanto, o valor final do Col_Balance está correto 100.
pacote com.cdai.orm.hibernate.transaction; importar org.hibernate.lockmode; importar org.hibernate.session; importar org.Hibernate.SessionFactory; importar org.hibernate.transaction; importar com.cdai.orm.hibernate.annotation.account; importação com.cdai.orm.hibernate.annotation.annotationHibernate; classe pública Upgradelock {@suppresswarnings ("deprecação") public static void main (string [] args) {final sessionFactory sessionFactory = anoTationHibernate.CreateSessionFactory (); // Executar transação 1 thread t1 = new Thread () {@Override public void run () {session1 = sessionFactory.opensession (); Transação tx1 = null; tente {tx1 = session1.begIntransaction (); System.out.println ("T1 - BEGIN Trasaction"); Thread.sleep (500); Conta de conta = (conta) session1.get (conta.class, new long (1), lockmode.upgrade); System.out.println ("t1 - balance =" + conta.getBalance ()); Thread.sleep (500); conta.setBalance (conta.getBalance () + 100); System.out.println ("T1 - Alterar o equilíbrio:" + conta.getBalance ()); tx1.Commit (); System.out.println ("T1 - Committ Transaction"); Thread.sleep (500); } catch (Exceção e) {e.printStackTrace (); if (tx1! = null) tx1.rollback (); } finalmente {session1.close (); }}}; // Executar transação 2 thread t2 = new Thread () {@Override public void run () {session2 = sessionFactory.opensession (); Transação tx2 = nulo; tente {tx2 = session2.begIntransaction (); System.out.println ("T2 - BEGIN Trasaction"); Thread.sleep (500); Conta da conta = (conta) session2.get (conta.class, new long (1), lockmode.upgrade); System.out.println ("t2 - balance =" + conta.getBalance ()); Thread.sleep (500); conta.setBalance (conta.getBalance () - 100); System.out.println ("T2 - Alterar o equilíbrio:" + conta.getBalance ()); tx2.Commit (); System.out.println ("T2 - Committ Transaction"); Thread.sleep (500); } catch (Exceção e) {e.printStackTrace (); if (tx2! = null) tx2.rollback (); } finalmente {session2.close (); }}}}; t1.start (); t2.start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } catch (interruptedException e) {}} System.out.println ("T1 e T2 estão mortos."); sessionFactory.close (); }}T1 - BEGIN TRASActionT2 - BEGIN TRASActionHIBERNATE: SELECIONE ACCLEGRA_.COL_ID AS COL1_0_0_, CONSCRUÇÃO0_.COL_BALANCE COM COL2_0_0_ DE TB_ACCOUNTED0_ com (Updlock, Rowlock) onde a conta0_.col_id =? col2_0_0_ de tb_account conta0_ com (updlock, rowlock) onde conta0_.col_id =? t2 - balance = 100t2 - altere o equilíbrio: 0hibernate: atualizar tb_account Set col_balance =? Onde col_id =? T2 - Commitem transactionT1 - Balance = 0t1 - Alterar Balance: 100HiBernate: Atualizar tb_account Set col_balance =? onde col_id =? T1 - Compromer a transação de T1 e T2 estão mortos.
Hibernate executa o SQL para SQLSERVER 2005:
A cópia do código é a seguinte:
Selecione Account0_.col_id como col1_0_0_, conta0_.col_balance como col2_0_0_ de tb_account conta0_ com (updlock, rowlock) onde conta0_.col_id =?
2. Bloqueio otimista: suponha que os dados da operação de transação atual não sejam acessados ao mesmo tempo por outras transações; portanto, o nível de isolamento do banco de dados é completamente confiado no banco de dados para gerenciar automaticamente o trabalho do bloqueio. Adote o controle da versão em aplicativos para evitar problemas de simultaneidade que podem ocorrer com baixa probabilidade.
No Hibernate, use anotações da versão para definir o campo Número da versão.
Substitua o objeto da conta em Dirtylock pelo AccouplVersion e o outro código permanece inalterado e ocorre uma exceção quando a execução ocorre.
pacote com.cdai.orm.hibernate.transaction; importar javax.persistence.column; importar javax.persistence.Entity; importar javax.persistence.id; importar javax.persistence.table; importar javax.persistence.version; @Entity @Table (name = "tb_account_version") public class Class AccountVersion {@id @column (name = "col_id") private longo id; @Column (name = "col_balance") Private Long Balance; @Version @column (name = "col_version") private int versão; public AccountVersion () {} public AccountVersion (Long ID, Longo Balanço) {this.id = id; this.Balance = Balance; } public long getId () {return id; } public void setId (longo id) {this.id = id; } public Long GetBalance () {Return Balance; } public void SetBalance (Longo Balance) {this.Balance = Balance; } public int getversion () {retornar versão; } public void setversion (int versão) {this.version = versão; }}O tronco é o seguinte:
T1 - BEGIN TRASActionT2 - BEGIN TRASACIONHIBERNATE: SELECT RECCLEVER0_.COL_ID AS COL1_0_0_, AccountVer0_.Col_Balance como col2_0_0_, AccountVerate0_.Col_VERSION AS COL3_0_0_ DE TB_ACCOUND_VERSION: COL1_0_0_, AccountVer0_.col_balance como col2_0_0_, accountver0_.col_version como col3_0_0_ de tb_account_version accountVer0_ where AccountVer0_.col_id =? col_balance =?, col_version =? onde col_id =? e col_version =? Hibernate: atualize tb_account_version Set col_balance =?, col_version =? onde col_id =? e col_version =? T1 - Commitem transação2264 [thread -2] erro org.hibernate.event.def.abstractLushingEventListener - não pôde sincronizar o estado do banco de dados com a sessionorg.hibernate.staleObjectstateException: a linha foi atualizada ou excluída por outra transação (ou não -value -valear [com.cdai.orm.hibernate.Transaction.AccountVersion#1] em org.hibernate.persister.entity.abstractentityPersister.check (abstractityPerSister.java :1934) em org.hibernate.persister.entity.abStractityPerity.UpDateNentity (abstractSister.AbStractity.234) em org.hibernate.persister.entity.abStractity: org.hibernate.persister.entity.abstractentityPerSister.UpDateInsert (AbstractEntityPerSister.java:2478) em org.hibernate.persister.entity.abstractentityPerSister.Update (abstractentityPersister.java:2805) org.hibernate.action.EntityUpDateAction.execute (entityUpDateAction.java:114) em org.hibernate.engine.actionQueue.execute (actionQueue.java:268) em org.hibernate.engine.actionQueue.executionEctions (actionue.java:20:266) em org.hibernate.engine.actionEue.executorEctions (actionue.java:266) org.hibernate.engine.actionQueue.executionActions (actionQueue.java:180) em org.hibernate.event.def.abstractLushingEventListener.performExecutions (abstrateflushingEventListener.java:321) em org.hibernate.Event.Def.DefaultfluhEventListener.onflush (defaultflushEventListener.java:51) em org.hibernate.impl.sessionImpl.flush5 (sessionImpl.java:1206) em org.ibernate.Impl.sessimPl.ManagedLhathLushPhushPhushPhushPhushPhath (SessionImpl.java:1206) em org.ibernate.ima: org.hibernate.transaction.jdbctransaction.Commit (jdbctransaction.java:137) em com.cdai.orm.hibernate.transaction.versionlock $ 2.run (versãolock.java:93) ambos T1 e T2 estão mortos.
Como o bloqueio otimista deixa completamente o isolamento da transação no banco de dados para controle, as transações 1 e 2 executam cruzamento, a transação 1 é cometida com sucesso e o Col_version é alterado para 1. No entanto, quando a transação 2 se compromete, os dados com col_version de 0 não podem mais ser encontrados, uma exceção foi lançada.
Comparação dos métodos de consulta de hibernato
Existem três métodos principais de consulta para Hibernate:
1.HQL (linguagem de consulta Hibernate)
É muito parecido com o SQL e suporta recursos como paginação, conexão, agrupamento, funções de agregação e subconsência.
Mas o HQL é orientado a objetos, não tabelas em bancos de dados relacionais. Como as declarações de consulta são orientadas para objetos de domínio, o uso de HQL pode obter benefícios de plataforma cruzada. Hibernado
Ele nos ajudará automaticamente a se traduzir em diferentes instruções SQL de acordo com diferentes bancos de dados. Isso é muito conveniente nos aplicativos que precisam suportar vários bancos de dados ou migrações de banco de dados.
Mas, apesar de fazer isso conveniente, como as instruções SQL são geradas automaticamente pelo Hibernate, isso não é propício à otimização e depuração da eficiência das instruções SQL. Quando a quantidade de dados é grande, pode haver problemas de eficiência.
Se houver um problema, não é conveniente investigar e resolvê -lo.
2.qbc/qbe (consulta por critérios/exemplo)
QBC/QBE executa consulta montando condições de consulta ou objetos de modelo. Isso é conveniente em aplicações que requerem suporte flexível para muitas combinações gratuitas de condições de consulta. O mesmo problema é que, como a declaração de consulta é montada livremente, o código para criar uma instrução pode ser longo e contém muitas condições de ramificação, o que é muito inconveniente para otimização e depuração.
3.SQL
O Hibernate também suporta métodos de consulta que executam diretamente o SQL. Este método sacrifica as vantagens do hibernato de dados cruzados e escreve manualmente as declarações SQL subjacentes para alcançar a melhor eficiência de execução.
Comparado com os dois primeiros métodos, a otimização e a depuração são mais convenientes.
Vamos dar uma olhada em um conjunto de exemplos simples.
pacote com.cdai.orm.hibernate.query; importar java.util.arrays; importar java.util.list; importar org.hibernate.criteria; importar org.hibernate.Query; importar org.hibernate.session; importar org.Hibernate.SessionFactory; importar org.hibernate.cfg.annotationConfiguration; importar org.hibernate.criterion.critrion; importar org.hibernate.critriot.example; importar org.hibernate.critrion.expression; importar com.cdai.orm.hibernate.annotation.account; public class BasicQuery {public static void main (string [] args) {sessionFactory SessionFactory = new AnoTationConfiguration (). addfile ("hibernate/hibernate.cfg.xml"). configure (). addPackage ("com.cdai.orm.hibernate.annotation"). addannotatedclass (conta.class). buildSessionFactory (); Sessão session = sessionFactory.opensssion (); // 1.hql Query Query = session.createquery ("da conta como um where ah =: id"); query.setlong ("id", 1); Resultado da lista = query.list (); para (linha do objeto: resultado) {System.out.println (linha); } // 2.qbc critérios critérios = session.createcriteria (conta.class); critério.add (expressão.eq ("id", novo long (2))); resultado = critério.List (); para (linha do objeto: resultado) {System.out.println (linha); } // 3.qbe conta de conta = new Account (); exemplo.setBalance (100); resultado = session.createcriteria (conta.class). add (exemplo.create (exemplo)). lista(); para (linha do objeto: resultado) {System.out.println (linha); } // 4.SQL Query = session.CreatesqlQuery ("Selecione Top 10 * no TB_ACCOUNT Ordem por col_id desc"); resultado = query.list (); para (linha do objeto: resultado) {System.out.println (Arrays.toString (((objeto []) linha)); } session.close (); }}Hibernate: selecione Account0_.col_id como col1_0_, conta0_.col_balance como col2_0_ de tb_account conta0_ onde conta0_.col_id =? this_.col_id =? conta [id = 2, balance = 100] hibernate: selecione this_.col_id como col1_0_0_, this_.col_balance como col2_0_0_0_ de tb_account this (this_.col_balance =) conta [id = 1, balanço = 100] [IDS = 2) Desc [2, 100] [1, 100]
A partir do log, você pode ver claramente o controle da Hibernate sobre as instruções SQL geradas. O método de consulta específico a ser escolhido depende do aplicativo específico.