Hibernate- und Datenbankschloss 1. Warum Schlösser verwenden?
Um herauszufinden, warum der Sperrmechanismus existiert, müssen Sie zunächst das Konzept der Transaktionen verstehen.
Eine Transaktion ist eine Reihe verwandter Operationen in einer Datenbank und muss saure Eigenschaften aufweisen:
Unsere häufig verwendete relationale Datenbank -RDBMs implementiert diese Eigenschaften von Transaktionen. Unter ihnen, Atomizität,
Konsistenz und Beharrlichkeit werden durch Holzeinschlag garantiert. Die Isolation wird durch den Verriegelungsmechanismus erreicht, über den wir heute besorgt sind, weshalb wir den Verriegelungsmechanismus benötigen.
Wenn es keine Schloss und keine Kontrolle über die Isolation gibt, welche Konsequenzen können verursacht werden?
Schauen wir uns das Beispiel von Hibernate an. Zwei Threads starten zwei Transaktionsvorgänge. Die gleiche Datenreihe in der Tabelle TB_ACCOUNT ist col_id = 1.
Paket com.cdai.orm.hiberNate.Annotation; importieren java.io.serializable; import Javax.Persistence.Column; import Javax.Persistence.Entity; import Javax.Persistence.id; import Javax.Persistence.table; @Entity @table (name = "tb_account") öffentliches Klasse -Konto implementiert serialisierbar {private statische endgültige long serialversionuid = 5018821760412231859L; @Id @column (name = "col_id") private long id; @Column (name = "col_balance") Private Long Balance; public account () {} public account (Long ID, langer Guthaben) {this.id = id; this.balance = balance; } public long getid () {return id; } public void setId (Long id) {this.id = id; } public Long getBalance () {Return Balance; } public void setbalance (langes Gleichgewicht) {this.balance = balance; } @Override public String toString () {return "account [id =" + id + ", Balance =" + Balance + "]"; }} Paket com.cdai.orm.hibernate.transaction; import org.hibernate.session; import org.hiberNate.sessionFactory; import org.hibernate.transaction; import org.hibernate.cfg.annotationConfiguration; import com.cdai.orm.hibernate.annotation.account; öffentliche Klasse DirtyRead {public static void main (String [] args) {endgültig SessionFactory Sessionfactory = new AnnotationConfiguration (). Addfile ("Hibernate/hibernate.cfg.xml"). konfigurieren(). addpackage ("com.cdai.orm.hibernate.annotation"). AddannotatedClass (Account.Class). BuildSessionFactory (); Thread t1 = neuer Thread () {@Override public void run () {Session Session1 = SessionFactory.OpenSession (); Transaktion TX1 = NULL; try {tx1 = session1.beginTransaction (); System.out.println ("t1 - begin trasaction"); Thread.Sleep (500); Account Account = (Konto) Sitzung1.get (Konto.Class, New Long (1)); System.out.println ("T1 - Balance =" + Account.getBalance ()); Thread.Sleep (500); Account.SetBalance (Account.getBalance () + 100); System.out.println ("T1 - Änderungsbilanz:" + Account.getBalance ()); tx1.commit (); System.out.println ("T1 - Commit Transaction"); Thread.Sleep (500); } catch (Ausnahme e) {e.printstacktrace (); if (tx1! = null) tx1.rollback (); } endlich {Session1.CLOSE (); }}}}; // 3.run Transaction 2 Thread t2 = neuer Thread () {@Override public void run () {session session2 = sessionFactory.OpenSession (); Transaktion TX2 = NULL; try {tx2 = session2.BeginTransaction (); System.out.println ("t2 - begin trasaction"); Thread.Sleep (500); Account Account = (Konto) Session2.get (Konto.Class, New Long (1)); System.out.println ("T2 - Balance =" + Account.getBalance ()); Thread.Sleep (500); Account.SetBalance (Account.getBalance () - 100); System.out.println ("T2 - Änderungsbilanz:" + Account.getBalance ()); tx2.commit (); System.out.println ("T2 - Commit -Transaktion"); Thread.Sleep (500); } catch (Ausnahme e) {e.printstacktrace (); if (tx2! = null) tx2.rollback (); } endlich {Session2.CLOSE (); }}}}; t1.start (); t2.Start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } catch (InterruptedException e) {}} System.out.println ("beide T1 und T2 sind tot."); SessionFactory.close (); }} Transaktion 1 reduziert Col_Balance um 100, während die Transaktion 2 sie um 100 reduziert, das Endergebnis kann 0 oder 200 sein und die Aktualisierung der Transaktion 1 oder 2 kann verloren gehen. Die Protokollausgabe bestätigt dies auch, Transaktionen 1 und 2
Log Cross-Print.
T1 - begin trasactiont2 - begin trasactionhiberNate: select account0_.col_id as col1_0_0_, account0_.col_balance as col2_0_0_ aus tb_account account0_, wobei Account0_.col_id =? Hibernate: Account0_Col_Id as col1_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0S -. TB_ACCOUNT ACOCT0_ WHERE ACOCORT0_.COL_ID =? T1 - SALAGE = 100T2 - SALAGE = 100T2 - Saldo ändern: 0T1 - Guthaben ändern: 200HiNRNATE: Aktualisieren Sie tb_account set col_balance =? Wo col_id =? hibernate: aktualisieren tb_account set col_balance =? wo col_id =?
Es ist ersichtlich, dass Isolation eine Angelegenheit ist, die sorgfältig berücksichtigt werden muss und es notwendig ist, Schlösser zu verstehen.
2. Wie viele Arten von Schlösser gibt es?
Gemeinsame werden gemeinsam genutzte Sperren, Aktualisierungssperrungen und exklusive Schlösser sind.
1. Shared Lock: Wird zum Lesen von Datenvorgängen verwendet, sodass andere Transaktionen gleichzeitig gelesen werden können. Wenn eine Transaktion eine Auswahlanweisung ausführt,
Die Datenbank weist der Transaktion automatisch eine gemeinsame Sperre zu, um die Lesedaten zu sperren.
2. Exklusive Sperre: Zum Ändern von Daten können andere Transaktionen nicht gelesen oder geändert werden. Wenn die Transaktion einfügen,
Beim Aktualisieren und Löschen wird die Datenbank automatisch zugewiesen.
3. Update -Sperre: Wird verwendet, um Deadlocks zu vermeiden, die durch gemeinsame Sperren während der Aktualisierungsvorgänge verursacht werden, wie z. Bei der Durchführung von Updates erwerbt die Transaktion zunächst das Update -Sperre und wird dann die Update -Sperre auf ein exklusives Sperre aktualisiert, wodurch ein Deadlock vermieden wird.
Darüber hinaus können diese Schlösser alle auf verschiedene Objekte in der Datenbank angewendet werden, d. H. Diese Schlösser können unterschiedliche Granularitäten aufweisen.
Z. B. Sperren auf Datenbankebene, Sperren auf Tabellenebene, Sperre auf Seitenebene, Schlüssellöser und Zeilenspiegel.
Es gibt also viele Arten von Schlössern. Es ist zu schwierig, so viele Schlösser flexibel zu beherrschen und zu verwenden. Wir sind keine DBAs.
was zu tun? Glücklicherweise ist der Sperrmechanismus für normale Benutzer transparent. Die Datenbank fügt automatisch geeignete Sperren hinzu und upgradiert und fällt automatisch zum richtigen Zeitpunkt auf. Es ist so nachdenklich! Alles, was wir tun müssen, ist zu lernen, das Isolationsniveau nach unterschiedlichen Geschäftsbedürfnissen festzulegen.
3. Wie kann man die Isolationsstufe festlegen?
Im Allgemeinen bietet das Datenbanksystem vier Transaktions -Isolationsstufen für Benutzer zur Auswahl:
1.Serialisierbar: Wenn zwei Transaktionen gleichzeitig dieselben Daten manipulieren, kann Transaktion 2 nur anhalten und warten.
2. Wiederholbares Lesen (wiederholbar): Transaktion 1 kann neu eingefügte Daten aus der Transaktion 2 angezeigt und keine Aktualisierungen für vorhandene Daten angezeigt werden.
3.Read Engages (Leseded -Daten): Transaktion 1 kann neu eingefügte und aktualisierte Daten aus der Transaktion 2 angezeigt werden.
4. Lesen Sie nicht übereinstimmende (gelesene nicht übereinstimmende Daten): Transaktion 1 kann Insertion und Aktualisierung von Daten sehen, die Transaktion 2 nicht begangen hat.
4. Schließe in der Anwendung
Wenn die Datenbank die Isolationsebene der Lesekommission annimmt, können pessimistische Sperren oder optimistische Sperren in der Anwendung verwendet werden.
1. Pessimistische Sperre: Angenommen, die Daten des aktuellen Transaktionsvorgangs haben definitiv einen anderen Transaktionszugriff. Geben Sie daher pessimistisch an, dass die ausschließliche Sperre in der Anwendung zur Sperrung der Datenressourcen verwendet wird. Unterstützen Sie die folgenden Formulare in MySQL und Oracle:
Wählen Sie ... für die Aktualisierung
Lassen Sie die ausgewählte Verwendung der exklusiven Sperre ausdrücklich ausgewählt, um die Datensätze der Abfrage zu sperren. Damit andere Transaktionen zur Abfrage, Aktualisierung oder Löschen dieser gesperrten Daten abfragen, aktualisieren oder löschen müssen, müssen sie warten, bis die Transaktion vorbei ist.
In Hibernate können Sie beim Laden in lockMode.upgrade übergeben, um pessimistisches Schloss anzuwenden. Ändern Sie das vorherige Beispiel,
Bei den GET -Methodenaufrufen der Transaktionen 1 und 2 wird ein zusätzlicher Parameter für den Lockmode übergeben. Wie aus dem Protokoll, den Transaktionen 1 und 2 ersichtlich ist
Es ist nicht mehr überquert, Transaktion 2 kann darauf warten, dass Transaktion 1 vor dem Lesen der Daten abgeschlossen ist. Der endgültige COL_Balance-Wert ist also korrekt 100.
Paket com.cdai.orm.hibernate.transaction; import org.hibernate.lockmode; import org.hibernate.session; import org.hiberNate.sessionFactory; import org.hibernate.transaction; import com.cdai.orm.hibernate.annotation.account; import com.cdai.orm.hibernate.Annotation.AnnotationhiberNate; öffentliche Klasse upgradelock {@SuppressWarnings ("Abschaltung") public static void main (String [] args) {endgültige sessionsfactory sessionfactory = AnnotationHiNNate.CreateSessionFactory (); // Transaktion 1 Thread t1 = neuer Thread () {@Override public void run () {Session Session1 = SessionFactory.OpenSession (); Transaktion TX1 = NULL; try {tx1 = session1.beginTransaction (); System.out.println ("t1 - begin trasaction"); Thread.Sleep (500); Account Account = (Konto) Sitzung1.get (Konto.Class, New Long (1), lockMode.Upgrade); System.out.println ("T1 - Balance =" + Account.getBalance ()); Thread.Sleep (500); Account.SetBalance (Account.getBalance () + 100); System.out.println ("T1 - Änderungsbilanz:" + Account.getBalance ()); tx1.commit (); System.out.println ("T1 - Commit Transaction"); Thread.Sleep (500); } catch (Ausnahme e) {e.printstacktrace (); if (tx1! = null) tx1.rollback (); } endlich {Session1.CLOSE (); }}}; // Transaktion 2 Thread T2 = neuer Thread () {@Override public void run () {Session Session2 = SessionFactory.OpenSession (); Transaktion TX2 = NULL; try {tx2 = session2.BeginTransaction (); System.out.println ("t2 - begin trasaction"); Thread.Sleep (500); Account Account = (Konto) Session2.get (Konto.Class, New Long (1), lockMode.Upgrade); System.out.println ("T2 - Balance =" + Account.getBalance ()); Thread.Sleep (500); Account.SetBalance (Account.getBalance () - 100); System.out.println ("T2 - Änderungsbilanz:" + Account.getBalance ()); tx2.commit (); System.out.println ("T2 - Commit -Transaktion"); Thread.Sleep (500); } catch (Ausnahme e) {e.printstacktrace (); if (tx2! = null) tx2.rollback (); } endlich {Session2.CLOSE (); }}}}; t1.start (); t2.Start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } catch (InterruptedException e) {}} System.out.println ("beide T1 und T2 sind tot."); SessionFactory.close (); }}T1 - Beginnen Sie Trasactiont2 - Beginnen Sie TrasactionHiBERNATE: Wählen Sie Account0_.col_id as col1_0_0_, account0_.col_balance as col2_0_0_ aus tb_account account0_ mit (updlock, rowlock) wobei Account0_.col_id =? col2_0_0_ aus TB_ACCOUNT ACOCORT0_ WITH (Updlock, Rowlock) WHERE ACOCORT0_.COL_ID =? T2 - Balance = 100T2 - Ausgleich ändern: 0HiNRNATE: Aktualisieren Sie TB_ACCOUNT set col_balance =? Wo col_id =? t2 - comment TransactionT1 - Balance = 0T1 - Änderungsbilanz: 100HiNRNATE: Aktualisieren Sie TB_ACCOUNT SET col_balance =? Wo col_id =? t1 - comment TransactionBoth T1 und T2 sind tot.
Hibernate führt SQL für SQLServer 2005 aus:
Die Codekopie lautet wie folgt:
Wählen Sie Account0_.col_id als col1_0_0_, account0_.col_balance as col2_0_0_ aus TB_ACCOUNT ACOCT0_ WITH (UPDOCK, ROWLOCK) WHERE ACOCT0_.COL_ID =?
2. Optimistische Sperre: Nehmen Sie an, dass die Daten des aktuellen Transaktionsvorgangs nicht gleichzeitig mit anderen Transaktionen zugegriffen werden, sodass auf die Isolationsstufe der Datenbank vollständig auf die Datenbank angewiesen ist, um die Arbeiten der Sperre automatisch zu verwalten. Übernehmen Sie die Versionskontrolle in Anwendungen, um Probleme mit der Parallelität zu vermeiden, die bei geringer Wahrscheinlichkeit auftreten können.
Verwenden Sie in Hibernate Versionsanmerkungen, um das Feld der Versionsnummer zu definieren.
Ersetzen Sie das Kontoobjekt in DirtyLock durch Recoversion, und der andere Code bleibt unverändert, und eine Ausnahme tritt auf, wenn die Ausführung auftritt.
Paket 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") private long id; @Column (name = "col_balance") Private Long Balance; @Version @Column (name = "col_version") private int Version; public AccountVersion () {} public accountversion (Long ID, langer Saldo) {this.id = id; this.balance = balance; } public long getid () {return id; } public void setId (Long id) {this.id = id; } public Long getBalance () {Return Balance; } public void setbalance (langes Gleichgewicht) {this.balance = balance; } public int getversion () {return Version; } public void setversion (int Version) {this.version = Version; }}Das Protokoll lautet wie folgt:
T1 - Beginnen Sie Trasactiont2 - Beginnen Sie TrasactionHiBERNATE: Wählen Sie Accountver0_.col_id als col1_0_0_, ycover0_.col_balance as col2_0_0_, accountver0_.col_version as col3_0_0_ aus tb_account_version accountver0_.col_col_ID =? col1_0_0_, accountver0_.col_balance as col2_0_0_, accountver0_.col_version as col3_0_0_ from tb_account_version accountver0_ where accountver0_.col_id=?T1 - balance=1000T2 - balance=1000T1 - Change balance:900T2 - Change balance:1100Hibernate: update tb_account_version set col_balance =?, col_version =? Wo col_id =? und col_version =? Wo col_id =? und col_version =? [com.cdai.orm.hibernate.transaction.accountversion#1] at org.hibernate.entity.abstractentityPersist. Check (AbstractentityPersister.java:1934) bei org.hibernate.perssist.abstractentitys.update (Abstractentity.AbScractentitys.UpTer.update.update.update.update.update.update.update.update.update.update.update.updatate (Abstractentitys) (Abstractentity.25). org.hibernate.Persistel.entity.abstractentityPersistel.UpdateOrinsert (AbstractentityPersist.java:2478) bei org.hiNNATE.PERSSTISCH.EENTITY.ABSTRACTENTITYPERSSTRAUS.UPDATE (AbstractentityPersist.java:2805) AttentityPersist. org.hibernate.action.entityUpdateAction.execute (entityUpDateAction.java:114) at org.hibernate.engine.ActionQueue.execute (actionQueue.java:268) at org.hibernate.EctionQueue.executeactions (actionqueue.java.java:260) at ActionQuue (actionqueue.java.java:260) at Actionqueue (actionqueue.java.java org.hibernate.engine.actionQueue.executeactions (actionqueue.java:180) bei org.hibernate.event.def.abstractFlushingEventListener.performexecutions (AbstractFlushingEventListListener.java:321) auf der org.hibernate.event.def.defaultflushEventListener.onflush (defaultFlushEventListener.java:51) bei org.hiNNate.impl.SessionImpl.flush (sessionImpl.java:1206) auf org.hibernate.impl.SessionImpl.ManPl.ManPl.Manit.managing.managing.managing.Manit.Manit.Manit.Manit.Manit.Manit.Manit.Manit.Manit.Manit.Manit.Manit.Manit.Manit.Managing org.hibernate.transaction.jdbctransaction.commit (jdbctransaction.java:137) unter com.cdai.orm.hibernate.transaction.versionLock $ 2.run (VersionLock.java:93) Beide T1 und T2 sind tot.
Da die optimistische Sperre die Transaktionsisolation für die Steuerung vollständig in die Datenbank verlässt, werden die Transaktionen 1 und 2 kreuzreihig ausgeführt, Transaktion 1 wird erfolgreich verpflichtet und Col_version wird jedoch auf 1 geändert. Wenn sich die Transaktion 2 verpflichtet, können Daten mit Col_version von 0 nicht mehr gefunden werden, sodass eine Ausnahme ausgelöst wurde.
Vergleich von Query -Methoden für Hibernate
Es gibt drei Hauptquery -Methoden für Hibernate:
1.HQL (Hibernate -Query -Sprache)
Es ist SQL sehr ähnlich und unterstützt Funktionen wie Paging, Verbindung, Gruppierung, Aggregationsfunktionen und Unterabfrage.
HQL ist jedoch objektorientiert, keine Tabellen in relationalen Datenbanken. Da Abfrageanweisungen auf Domänenobjekte ausgerichtet sind, kann die Verwendung von HQL plattformübergreifende Vorteile nutzen. Überwintern
Es hilft uns automatisch, in verschiedenen SQL -Anweisungen nach verschiedenen Datenbanken umzusetzen. Dies ist bei Anwendungen sehr bequem, die mehrere Datenbanken oder Datenbankmigrationen unterstützen müssen.
Da es zwar bequem wird, ist dies, da SQL -Anweisungen automatisch von Hibernate generiert werden, der Effizienzoptimierung und des Debuggens von SQL -Anweisungen nicht förderlich. Wenn die Datenmenge groß ist, kann es zu Effizienzproblemen kommen.
Wenn es ein Problem gibt, ist es nicht bequem, es zu untersuchen und zu lösen.
2.QBC/QBE (Abfrage nach Kriterien/Beispiel)
QBC/QBE führt Abfrage durch, indem Sie Abfragebedingungen oder Vorlagenobjekte zusammenstellen. Dies ist in Anwendungen, die eine flexible Unterstützung für viele kostenlose Kombinationen von Abfragebedingungen erfordern. Das gleiche Problem ist, dass der Code zum Erstellen einer Anweisung lang sein kann, da die Anweisung der Abfrage frei zusammengebaut wird und viele Verzweigungsbedingungen enthält, was für die Optimierung und Debuggierung sehr unpraktisch ist.
3.SQL
Hibernate unterstützt auch Abfragemethoden, die SQL direkt ausführen. Diese Methode opfert die Vorteile von Hibernate Cross-Database und schreibt manuell die zugrunde liegenden SQL-Aussagen, um die beste Ausführungseffizienz zu erreichen.
Im Vergleich zu den ersten beiden Methoden sind Optimierung und Debuggen bequemer.
Werfen wir einen Blick auf einfache Beispiele.
Paket com.cdai.orm.hibernate.Query; Import Java.util.Arrays; importieren java.util.list; import org.hibernate.criteria; import org.hibernate.query; import org.hibernate.session; import org.hiberNate.sessionFactory; import org.hibernate.cfg.annotationConfiguration; import org.hibernate.criterion.criterion; import org.hibernate.criterion.example; import org.hibernate.criterion.expression; import com.cdai.orm.hibernate.annotation.account; öffentliche Klasse BasicQuery {public static void main (String [] args) {SessionFactory Sessionfactory = new AnnotationConfiguration (). Addfile ("Hibernate/hibernate.cfg.xml"). konfigurieren(). addpackage ("com.cdai.orm.hibernate.annotation"). AddannotatedClass (Account.Class). BuildSessionFactory (); Session Session = SessionFactory.openSession (); // 1.hql query query = session.createquery ("aus dem Konto als wobei a.id =: id"); query.setlong ("id", 1); List ergebnis = query.list (); für (Objektzeile: Ergebnis) {System.out.println (row); } // 2.QBC -Kriterienkriterien = Sitzung.Createcriteria (Account.Class); Kriterien.Add (Expression.eq ("ID", New Long (2))); result = criteria.list (); für (Objektzeile: Ergebnis) {System.out.println (row); } // 3.qbe Account Beispiel = neuer Konto (); Beispiel.SetBalance (100); result = session.createcriteria (Account.Class). add (Beispiel.Create (Beispiel)). Liste(); für (Objektzeile: Ergebnis) {System.out.println (row); } // 4.sql query = session.createSQLQuery ("Top 10 * aus TB_ACCOUND Order by col_id Desc"); result = query.list (); für (Objektzeile: Ergebnis) {System.out.println (arrays.toString ((Objekt []) Zeile)); } Session.close (); }}Hibernate: Wählen Sie Account0_.col_id as col1_0_, account0_.col_balance as col2_0_ aus tb_account account0_ wob this_.col_id =? account [id = 2, balance = 100] hibernate: Wählen Sie diese_.col_id als col1_0_0_, this_.col_balance als col2_0_0_ aus tb_account this_ WHERE (this_.col_balance =?) Account [id = 1, Balance = 100] Account [id = 100]. Desc [2, 100] [1, 100]
Aus dem Protokoll sehen Sie die Kontrolle von Hibernate deutlich über die generierten SQL -Anweisungen. Die spezifische Auswahlmethode hängt von der spezifischen Anwendung ab.