Hibernate y bloqueo de la base de datos 1. ¿Por qué usar cerraduras?
Para averiguar por qué existe el mecanismo de bloqueo, primero debe comprender el concepto de transacciones.
Una transacción es una serie de operaciones relacionadas en una base de datos, y debe tener características ácidas:
Nuestra base de datos relacional de uso común implementa estas características de las transacciones. Entre ellos, atomicidad,
La consistencia y la persistencia están garantizadas por registro. El aislamiento se logra mediante el mecanismo de bloqueo que nos preocupa hoy, por lo que necesitamos el mecanismo de bloqueo.
Si no hay bloqueo ni control sobre el aislamiento, ¿qué consecuencias pueden ser causadas?
Echemos un vistazo al ejemplo de Hibernate. Dos hilos comienzan dos operaciones de transacción respectivamente. La misma fila de datos en la tabla TB_ACCOUNT es col_id = 1.
paquete com.cdai.orm.hibernate.annotation; import java.io.serializable; import javax.persistence.column; import javax.persistence.entity; import javax.persistence.id; import javax.persistence.table; @Entity @Table (name = "tb_account") La cuenta de clase pública implementa serializable {private static final long SerialVersionUid = 5018821760412231859l; @Id @column (name = "col_id") ID de largo privado; @Column (name = "col_balance") Balance largo privado; Cuenta pública () {} Cuenta pública (ID de largo, saldo largo) {this.id = id; this.balance = balance; } public Long Long getId () {return id; } public void setid (ID long) {this.id = id; } public Long GetBalance () {Balance de retorno; } public void setBalance (Long Balance) {this.balance = balance; } @Override public String toString () {return "cuenta [id =" + id + ", balance =" + balance + "]"; }} paquete com.cdai.orm.hibernate.transaction; importar org.hibernate.session; importar org.hibernate.sessionFactory; importar org.hibernate.transaction; importar org.hibernate.cfg.annotationConfiguration; import com.cdai.orm.hibernate.annotation.account; clase pública DirtyRead {public static void main (String [] args) {final sesión de sessionFactory = new AnnotationConfiguration (). AddFile ("Hibernate/Hibernate.cfg.xml"). configure (). addpackage ("com.cdai.orm.hibernate.annotation"). AddAnnotatedClass (Account.Class). BuildSessionFactory (); Thread t1 = new Thread () {@Override public void run () {session session1 = sessionFactory.opensession (); Transacción tx1 = nulo; intente {tx1 = session1.begintransaction (); System.out.println ("T1 - Begin Trasaction"); Thread.sleep (500); Cuenta de cuenta = (Cuenta) Session1.get (Account.Class, new Long (1)); System.out.println ("t1 - balance =" + cuenta.getBalance ()); Thread.sleep (500); Account.SetBalance (cuenta.getBalance () + 100); System.out.println ("T1 - Balance de cambio:" + Account.GetBalance ()); tx1.commit (); System.out.println ("T1 - transacción de confirmación"); Thread.sleep (500); } catch (Exception e) {E.PrintStackTrace (); if (tx1! = null) tx1.rollback (); } finalmente {session1.close (); }}}}; // 3.run Transaction 2 Thread t2 = new Thread () {@Override public void run () {session session2 = sessionFactory.opensession (); Transacción tx2 = nulo; intente {tx2 = session2.BeGinTransaction (); System.out.println ("T2 - Begin Trasaction"); Thread.sleep (500); Cuenta de cuenta = (Cuenta) Session2.get (Account.Class, new Long (1)); System.out.println ("t2 - balance =" + cuenta.getBalance ()); Thread.sleep (500); cuenta.setBalance (cuenta.getBalance () - 100); System.out.println ("T2 - Balance de cambio:" + Account.GetBalance ()); tx2.commit (); System.out.println ("T2 - Conjunto de la transacción"); Thread.sleep (500); } catch (Exception e) {E.PrintStackTrace (); if (tx2! = null) tx2.rollback (); } finalmente {session2.close (); }}}}; t1.start (); t2.start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } capt (interruptedException e) {}} System.out.println ("Tanto T1 como T2 están muertos"); sessionFactory.close (); }} La transacción 1 reduce Col_balance en 100, mientras que la transacción 2 la reduce en 100, el resultado final puede ser 0 o 200, y la actualización de la transacción 1 o 2 puede perderse. La salida del registro también confirma esto, las transacciones 1 y 2
Registro de la impresión cruzada.
T1 - Begin TrasActionT2 - Begin TrasActionHibernate: seleccione Account0_.Col_ID como COL1_0_0_, Account0_.Col_Balance como Col2_0_0_ Desde tb_account Account0_ donde cuenta0_.col_id =? Hibernate: seleccione Account0_.Col_id como Col1_0_, Account0_.Colance As COM tb_account cuenta0_ donde cuenta0_.col_id =? T1 - balance = 100t2 - balance = 100t2 - Cambiar saldo: 0t1 - Cambiar saldo: 200Hibernate: actualizar tb_account set col_balance =? ¿Dónde col_id =? Hibernate: actualizar tb_account set col_balance =? donde col_id =? T1 - Commit TransactionT2 - Conjunto de transacciones tanto T1 y T2 están muertos.
Se puede ver que el aislamiento es un asunto que necesita una consideración cuidadosa y es necesario comprender las cerraduras.
2. ¿Cuántos tipos de cerraduras hay?
Los comunes son cerraduras compartidas, cerraduras de actualización y cerraduras exclusivas.
1. Bloqueo compartido: utilizado para leer operaciones de datos, permitiendo que otras transacciones se lean simultáneamente. Cuando una transacción ejecuta una instrucción SELECT,
La base de datos asigna automáticamente un bloqueo compartido a la transacción para bloquear los datos de lectura.
2. LOQUEO EXCLUSIVO: Utilizado para modificar los datos, otras transacciones no se pueden leer o modificar. Cuando la transacción se ejecuta insertar,
Cuando se actualizan la actualización y el eliminación, la base de datos se asignará automáticamente.
3. Bloqueo de actualización: se usa para evitar los bloqueos muertos causados por los bloqueos compartidos durante las operaciones de actualización, como las transacciones 1 y 2 que contienen bloqueos compartidos al mismo tiempo y esperando obtener cerraduras exclusivas. Al realizar la actualización, la transacción primero adquiere el bloqueo de actualización y luego actualiza el bloqueo de actualización a un bloqueo exclusivo, evitando así un punto muerto.
Además, todos estos bloqueos se pueden aplicar a diferentes objetos en la base de datos, es decir, estos bloqueos pueden tener diferentes granularidades.
Tales como bloqueos a nivel de base de datos, bloqueos a nivel de mesa, bloqueos a nivel de página, bloqueos de nivel de llave y bloqueos de nivel de fila.
Entonces hay muchos tipos de cerraduras. Es demasiado difícil dominar completamente y usar tantas cerraduras de manera flexible. No somos DBA.
¿Qué hacer? Afortunadamente, el mecanismo de bloqueo es transparente para los usuarios comunes. La base de datos agregará automáticamente los bloqueos apropiados y actualizará y degradará automáticamente varios bloqueos en el momento adecuado. ¡Es tan atento! Todo lo que necesitamos hacer es aprender a establecer el nivel de aislamiento de acuerdo con las diferentes necesidades comerciales.
3. ¿Cómo establecer el nivel de aislamiento?
En términos generales, el sistema de base de datos proporciona cuatro niveles de aislamiento de transacciones para que los usuarios puedan elegir:
1.serializable: cuando dos transacciones manipulan los mismos datos al mismo tiempo, la transacción 2 solo puede detenerse y esperar.
2. Leer repetible (repetible): la transacción 1 puede ver datos recién insertados de la transacción 2 y no puede ver actualizaciones de los datos existentes.
3. Read Comediante (Read Datos comprometidos): La Transacción 1 puede ver datos recién insertados y actualizados de la Transacción 2.
4. Read no comprometido (lea datos no comprometidos): la transacción 1 puede ver la inserción y actualizar los datos que la transacción 2 no se ha cometido.
4. Se bloquea la aplicación
Cuando la base de datos adopta el nivel de aislamiento de la comisión de lectura, se pueden usar bloqueos pesimistas u bloqueos optimistas en la aplicación.
1. Bloqueo pesimista: suponga que los datos de la operación de transacción actual definitivamente tendrán otro acceso a la transacción, por lo que especifica pesimista que el bloqueo exclusivo se usa en la aplicación para bloquear los recursos de datos. Apoye los siguientes formularios en MySQL y Oracle:
Seleccione ... para actualizar
Deje explícitamente que el bloqueo selecto use bloqueo exclusivo para bloquear los registros de la consulta. Para otras transacciones para consultar, actualizar o eliminar estos datos bloqueados, deben esperar hasta que termine la transacción.
En Hibernate, puede pasar en LockMode. Upgrade al cargar para adoptar el bloqueo pesimista. Modificar el ejemplo anterior,
En las llamadas del método GET de las transacciones 1 y 2, se pasa un parámetro de bloqueo adicional. Como se puede ver en el registro, las transacciones 1 y 2
Ya no se transmite, la transacción 2 puede esperar a que la transacción 1 termine antes de leer los datos, por lo que el valor final de col_balance es correcto 100.
paquete com.cdai.orm.hibernate.transaction; importar org.hibernate.lockMode; importar org.hibernate.session; importar org.hibernate.sessionFactory; importar org.hibernate.transaction; import com.cdai.orm.hibernate.annotation.account; import com.cdai.orm.hibernate.annotation.annotationHibernate; public class UpdateLock {@SupplesSwarnings ("Deprecation") public static void main (string [] args) {final sessionFactory sessionFactory = annotationHibernate.createSessionFactory (); // Ejecutar transacción 1 hilo t1 = new Thread () {@Override public void run () {session session1 = sessionFactory.opensession (); Transacción tx1 = nulo; intente {tx1 = session1.begintransaction (); System.out.println ("T1 - Begin Trasaction"); Thread.sleep (500); Cuenta de cuenta = (cuenta) Session1.get (Account.Class, new Long (1), LockMode.upgrade); System.out.println ("t1 - balance =" + cuenta.getBalance ()); Thread.sleep (500); Account.SetBalance (cuenta.getBalance () + 100); System.out.println ("T1 - Balance de cambio:" + Account.GetBalance ()); tx1.commit (); System.out.println ("T1 - transacción de confirmación"); Thread.sleep (500); } catch (Exception e) {E.PrintStackTrace (); if (tx1! = null) tx1.rollback (); } finalmente {session1.close (); }}}; // Ejecutar Transaction 2 Thread t2 = new Thread () {@Override public void run () {session session2 = sessionFactory.opensession (); Transacción tx2 = nulo; intente {tx2 = session2.BeGinTransaction (); System.out.println ("T2 - Begin Trasaction"); Thread.sleep (500); Cuenta de cuenta = (cuenta) session2.get (cuenta.class, new Long (1), LockMode.upgrade); System.out.println ("t2 - balance =" + cuenta.getBalance ()); Thread.sleep (500); cuenta.setBalance (cuenta.getBalance () - 100); System.out.println ("T2 - Balance de cambio:" + Account.GetBalance ()); tx2.commit (); System.out.println ("T2 - Conjunto de la transacción"); Thread.sleep (500); } catch (Exception e) {E.PrintStackTrace (); if (tx2! = null) tx2.rollback (); } finalmente {session2.close (); }}}}; t1.start (); t2.start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } capt (interruptedException e) {}} System.out.println ("Tanto T1 como T2 están muertos"); sessionFactory.close (); }}T1 - Begin TrasActionT2 - Begin TrasActionHibernate: seleccione Account0_.Col_ID como COL1_0_0_, Account0_.Col_Balance como col2_0_0_ desde tb_account Account0_ con (Updlock, RowLock) donde cuenta0_.col_id =? HiBernate: Seleccione Account0_.Col_ID AS COL1_0_0_, COUNT COL2_0_0_ desde tb_account cuenta0_ con (Updlock, RowLock) donde cuenta0_.col_id =? T2 - balance = 100t2 - Cambiar saldo: 0Hibernate: Update tb_account set col_balance =? donde col_id =? T2 - Conjunto TransactionT1 - Balance = 0T1 - Balance de cambio: 100Hibernate: Update TB_ACCOUNT set col_balance =? donde col_id =? T1 - Comprometer la transacción tanto T1 como T2 están muertos.
Hibernate ejecuta SQL para SQLServer 2005:
La copia del código es la siguiente:
Seleccione Account0_.Col_id como COL1_0_0_, Account0_.Col_Balance como COL2_0_0_ desde tb_account Account0_ con (Updlock, RowLock) donde cuenta0_.col_id =?
2. Bloqueo optimista: suponga que no se accederá a los datos de la operación de transacción actual al mismo tiempo por otras transacciones, por lo que el nivel de aislamiento de la base de datos se basa completamente en la base de datos para administrar automáticamente el trabajo del bloqueo. Adopte el control de versiones en aplicaciones para evitar problemas de concurrencia que pueden ocurrir con baja probabilidad.
En Hibernate, use anotaciones de versión para definir el campo Número de versión.
Reemplace el objeto de cuenta en Dirtylock con contabilidad, y el otro código permanece sin cambios, y se produce una excepción cuando ocurre la ejecución.
paquete com.cdai.orm.hibernate.transaction; import javax.persistence.column; import javax.persistence.entity; import javax.persistence.id; import javax.persistence.table; import javax.persistence.version; @Entity @Table (name = "tb_account_version") Public Class Accountversion {@id @column (name = "col_id") ID de largo privado; @Column (name = "col_balance") Balance largo privado; @Version @column (name = "col_version") private int versión; Public Accountversion () {} Public Accountversion (ID largo, saldo largo) {this.id = id; this.balance = balance; } public Long Long getId () {return id; } public void setid (ID long) {this.id = id; } public Long GetBalance () {Balance de retorno; } public void setBalance (Long Balance) {this.balance = balance; } public int getVersion () {Versión de retorno; } public void setVersion (int versión) {this.version = versión; }}El registro es el siguiente:
T1 - BEGIN TRASACTIONT2 - BEGIN TrasActionHibernate: Seleccione Accountver0_.Col_ID como COL1_0_0_0_, Accountver0_.Col_Balance como Col2_0_0_, Accountver0_.Col_Version AS COL3_0_0_ de TB_Count_version Accoughver0_ Where Accountver0_.COLID =? HerConsate: HerCebernate: HerConsate: SELECT COL1_0_0_, Cuactover0_.col_balance como COL2_0_0_, Cuactover0_.col_version como COL3_0_0_ de TB_ACCOUNT_VERSION Accountver0_ donde cuentas_.col_id =? T1 - balance = 1000T2 - balance = 1000T1 - Balance de cambio: 900T2 - Cambiar saldo: 1100Hibernate: Update: update tbeD col_balance =?, col_version =? donde col_id =? y col_version =? Hibernate: actualizar tb_account_version set col_balance =?, col_version =? donde col_id =? y col_version =? T1 - Conjunto de transacción2264 [Thread -2] Error org.hibernate.event.def.abstractflushingEventListener - No se pudo sincronizar el estado de la base de datos con sessionorg.Hibernate.StaleObjectStateException: la fila se actualizó o se eliminó por otra transacción (o no se ha podado la mapeación de válvulas incovectadas): fue inchaludado))) [com.cdai.orm.hibernate.transaction.AccountVersion#1] at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1934) at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2578) at org.hibernate.persister.entity.AbstrtractIntityPersister.UpdateorInsert (AbstractEntityPersister.Java:2478) en org.hibernate.persister.entity.AbstrtractIntityPersister.Update (AbstractPersister.Java:2805) AT org.Hibernate.Action.EntityUpDatAction.ExCute (EntityUpDateAction.Java:114) en org.hibernate.engine.actionqueue.execute (Actionqueue.Java:268) en Org.Hibernate.engine.ActionQueue.executeactions (ActionQueue.Java:260) org.hibernate.engine.actionqueue.executeactions (acciónqueue.java:180) en org.hibernate.event.def.abstractflushingeventListener.performeCeCution org.hibernate.event.def.defaultflusheventListener.onflush (defaultFlusheventListener.java:51) en org.hibernate.impl.sessionImpl.flush (sessionImpl.Java:1206) en org.hibernate.Impl.sessionsImpL.ManedSeledflush (sessionM.Java:375) en org.hibernate.transaction.jdbctransaction.commit (jdbctransaction.java:137) en com.cdai.orm.hibernate.transaction.versionlock $ 2.run (versionlock.java:93) tanto T1 como T2 están muertos.
Dado que el bloqueo optimista deja por completo el aislamiento de la transacción a la base de datos para el control, las transacciones 1 y 2 ejecutan la carrera cruzada, la transacción 1 se comete con éxito y la col de col.
Comparación de métodos de consulta de hibernación
Hay tres métodos de consulta principales para Hibernate:
1.HQL (lenguaje de consulta hibernada)
Es muy similar a SQL y admite características como paginación, conexión, agrupación, funciones de agregación y subconsulta.
Pero HQL está orientado a objetos, no en tablas en bases de datos relacionales. Debido a que las declaraciones de consulta están orientadas a objetos de dominio, el uso de HQL puede obtener beneficios multiplataforma. Hibernar
Nos ayudará automáticamente a traducirnos en diferentes declaraciones SQL de acuerdo con diferentes bases de datos. Esto es muy conveniente en aplicaciones que necesitan admitir múltiples bases de datos o migraciones de bases de datos.
Pero si bien lo hace conveniente, ya que Hibernate generan automáticamente las declaraciones SQL, esto no es propicio para la optimización de eficiencia y la depuración de las declaraciones SQL. Cuando la cantidad de datos es grande, puede haber problemas de eficiencia.
Si hay un problema, no es conveniente investigarlo y resolverlo.
2.QBC/QBE (consulta por criterio/ejemplo)
QBC/QBE realiza consultas ensamblando condiciones de consulta o objetos de plantilla. Esto es conveniente en aplicaciones que requieren soporte flexible para muchas combinaciones gratuitas de condiciones de consulta. El mismo problema es que, dado que la declaración de consulta se ensambla libremente, el código para crear una declaración puede ser largo y contiene muchas condiciones de ramificación, lo cual es muy inconveniente para la optimización y la depuración.
3.sql
Hibernate también admite métodos de consulta que ejecutan directamente SQL. Este método sacrifica las ventajas de Hibernate Cross-Database y escribe manualmente las declaraciones SQL subyacentes para lograr la mejor eficiencia de ejecución.
En comparación con los dos primeros métodos, la optimización y la depuración son más convenientes.
Echemos un vistazo a un conjunto de ejemplos simples.
paquete com.cdai.orm.hibernate.query; importar java.util.arrays; import 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.criterion; importar org.hibernate.criterion.example; importar org.hibernate.criterion.expression; import com.cdai.orm.hibernate.annotation.account; public class BasicQuery {public static void main (string [] args) {sessionFactory sessionFactory = new AnnotationConfiguration (). AddFile ("Hibernate/Hibernate.cfg.xml"). configure (). addpackage ("com.cdai.orm.hibernate.annotation"). AddAnnotatedClass (Account.Class). BuildSessionFactory (); Sesión session = sessionFactory.opensession (); // 1.HQL consulta consulta = session.createQuery ("De la cuenta como un where a.id =: id"); query.setLong ("id", 1); Resultado de la lista = query.list (); for (objeto fila: resultado) {system.out.println (fila); } // 2.QBC Criterios Criterios = Session.CreateCriteria (Account.Class); criteria.add (expresion.eq ("id", nuevo largo (2))); resultado = criteria.list (); for (objeto fila: resultado) {system.out.println (fila); } // 3.qbe cuenta de cuenta = nueva cuenta (); Ejemplo.setBalance (100); resultado = session.createCriteria (cuenta.class). Agregar (ejemplo.create (ejemplo)). lista(); for (objeto fila: resultado) {system.out.println (fila); } //. resultado = query.list (); for (objeto fila: resultado) {system.out.println (arrays.toString ((objeto []) fila)); } session.close (); }}Hibernate: Seleccione Account0_.Col_id como Col1_0_, Account0_.Col_Balance como col2_0_ desde tb_account cuenta0_ donde cuenta0_.col_id =? Cuenta [id = 1, balance = 100] Hibernate: seleccione este_.col_id como col1_0_0_, this_.col_balance as COL2_0_ de TBUT this_.col_id =? cuenta [id = 2, balance = 100] Hibernate: seleccione this_.col_id como col1_0_0_0_, this_.col_balance como col2_0_0_ de tb_account this__ where (this_.col_balance =?) Cuenta [id = 1, balance = 100] Cuenta [id = 2, balance = 100] SELECT: SEEL SELEC Col_id Desc [2, 100] [1, 100]
Desde el registro, puede ver claramente el control de Hibernate sobre las declaraciones SQL generadas. El método de consulta específico para elegir depende de la aplicación específica.