Замок с зимством и база данных. Зачем использовать блокировки?
Чтобы выяснить, почему существует механизм блокировки, вы должны сначала понять концепцию транзакций.
Транзакция - это серия связанных операций в базе данных, и она должна иметь кислотные характеристики:
Наша обычно используемая реляционная база данных RDBMS реализует эти характеристики транзакций. Среди них атомность,
Последовательность и настойчивость гарантируются запуска. Изоляция достигается с помощью механизма блокировки, который мы обеспокоены сегодня, поэтому нам нужен механизм блокировки.
Если нет блокировки и контроля над изоляцией, какие последствия могут быть вызваны?
Давайте посмотрим на пример Hibernate. Две потоки начинают две операции транзакций соответственно. Та же самая строка данных в таблице TB_ACCOUNT составляет COL_ID = 1.
пакет com.cdai.orm.hibernate.annotation; импортировать java.io.serializable; Импорт javax.persistence.column; Импорт javax.persistence.entity; Импорт javax.persistence.id; Импорт javax.persistence.table; @Entity @Table (name = "TB_ACCOUNT") Общедоступная учетная запись класса реализует Serializable {Private Static Long Long SerialVersionUID = 5018821760412231859L; @Id @column (name = "col_id") private long id; @Column (name = "col_balance") частный длинный баланс; public account () {} public account (long id, long balance) {this.id = id; this.balance = баланс; } public long getId () {return id; } public void setId (long id) {this.id = id; } public long getBalance () {return Balance; } public void setBalance (длинный баланс) {this.balance = balance; } @Override public String toString () {return "account [id =" + id + ", balance =" + balance + "]"; }} пакет com.cdai.orm.hibernate.transaction; Импорт org.hibernate.session; Импорт org.hibernate.sessionFactory; Импорт org.hibernate.transaction; Импорт org.hibernate.cfg.annotationConfiguration; Импорт com.cdai.orm.hibernate.annotation.account; открытый класс DirtyRead {public static void main (string [] args) {final SessionFactory SessionFactory = new AnnotationConfiguration (). addfile ("hibernate/hibernate.cfg.xml"). configure (). addpackage ("com.cdai.orm.bhint.nannotation"). AddannotatedClass (Account.Class). buildSessionFactory (); Поток t1 = new Thread () {@Override public void run () {session1 = sessionFactory.opensession (); Транзакция tx1 = null; try {tx1 = session1.begintransaction (); System.out.println ("T1 - Begin Trasaction"); Thread.sleep (500); Учетная запись = (учетная запись) session1.get (account.class, new long (1)); System.out.println ("t1 - balance =" + account.getBalance ()); Thread.sleep (500); Account.SetBalance (account.getBalance () + 100); System.out.println ("T1 - изменение баланса:" + account.getBalance ()); tx1.commit (); System.out.println ("T1 - Compet Transaction"); Thread.sleep (500); } catch (Exception e) {e.printstackTrace (); if (tx1! = null) tx1.rollback (); } наконец {session1.close (); }}}}; // 3.run Transaction 2 Thread T2 = New Thread () {@Override public void run () {session2 = sessionFactory.opensession (); Транзакция tx2 = null; try {tx2 = session2.begintransaction (); System.out.println ("T2 - Begin Trasaction"); Thread.sleep (500); Учетная запись = (учетная запись) session2.get (account.class, new long (1)); System.out.println ("t2 - balance =" + account.getBalance ()); Thread.sleep (500); Account.SetBalance (account.getBalance () - 100); System.out.println ("T2 - изменение баланса:" + account.getBalance ()); tx2.commit (); System.out.println ("T2 - Compet Transaction"); Thread.sleep (500); } catch (Exception e) {e.printstackTrace (); if (tx2! = null) tx2.rollback (); } наконец {session2.close (); }}}}; t1.start (); t2.start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } catch (прерывание Exception e) {}} System.out.println ("как T1, так и T2 мертвы."); sessionFactory.Close (); }} Транзакция 1 уменьшает COL_BALANCE на 100, в то время как транзакция 2 уменьшает ее на 100, конечный результат может быть 0 или 200, а обновление транзакции 1 или 2 может быть потеряно. Вывод журнала также подтверждает это, транзакции 1 и 2
бревенчатый припечаток.
T1 - Begin Trasactiont2 - Begin TrasactionHibernate: Select Account0_.col_id as col1_0_0_, account0_.col_balance as col2_0_0_ из tb_account account0_ где account0_.col_id =? TB_ACCOUNT Account0_ где account0_.col_id =? T1 - Balance = 100t2 - Balance = 100T2 - изменение Баланса: 0t1 - Изменение Баланс: 200 Гиберт: обновление tb_account set col_balance =? где col_id =? Hibernate: обновить tb_account set col_balance =? где col_id =? T1 - Commit Transactiont2 - Транзакция COMMEPONT T1 и T2 мертвы.
Видно, что изоляция является вопросом, который требует тщательного рассмотрения, и необходимо понимать замки.
2. Сколько там типов замков?
Общие - общие блокировки, блокировки обновлений и эксклюзивные блокировки.
1. Общая блокировка: используется для чтения операций данных, что позволяет прочитать другие транзакции одновременно. Когда транзакция выполняет оператор SELECT,
База данных автоматически присваивает общую блокировку транзакции для блокировки данных чтения.
2. Эксклюзивная блокировка: используется для изменения данных, другие транзакции не могут быть прочитаны или изменены. Когда транзакция выполняет вставку,
При обновлении и удалении обновляются, база данных будет автоматически распределена.
3. Обновление блокировки: используется, чтобы избежать тупиков, вызванных общими блокировками во время операций обновления, таких как транзакции 1 и 2, удерживающие общие замки одновременно и ожидают получения эксклюзивных замков. При выполнении обновления транзакция сначала приобретает блокировку обновления, а затем обновляет блокировку обновления до эксклюзивной блокировки, что позволяет избежать тупика.
Кроме того, все эти блокировки могут быть применены к различным объектам в базе данных, то есть эти замки могут иметь разные зернистости.
Такие как блокировки уровня базы данных, блокировки на уровне таблицы, блокировки на уровне страницы, блокировки уровня клавиш и блокировки уровня строк.
Так что есть много типов замков. Слишком сложно полностью освоить и гибко использовать так много замков. Мы не DBAS.
что делать? К счастью, механизм блокировки прозрачен для обычных пользователей. База данных автоматически добавит соответствующие блокировки и автоматически обновлять и понижать различные блокировки в нужное время. Это так задумчиво! Все, что нам нужно сделать, это научиться устанавливать уровень изоляции в соответствии с различными потребностями бизнеса.
3. Как установить уровень изоляции?
Вообще говоря, система базы данных предоставляет четыре уровня изоляции транзакций для пользователей на выбор:
1. Основной: когда две транзакции манипулируют одними и теми же данными одновременно, транзакция 2 может только остановиться и ждать.
2. Поднимаемый чтение (повторяется): Транзакция 1 может видеть вновь вставленные данные из Транзакции 2 и не может видеть обновления существующих данных.
3. Прочитайте совершенные (чтение фирменных данных): Транзакция 1 может видеть вновь вставленные и обновленные данные из Транзакции 2.
4. Прочитайте нерешенные (прочитайте незавершенные данные): Транзакция 1 может видеть данные вставки и обновления, которые транзакция 2 не совершена.
4. блокировки в приложении
Когда база данных принимает уровень изоляции чтения Комиссии, в приложении могут использоваться пессимистические замки или оптимистичные блокировки.
1. Пессимистическая блокировка: Предположим, что данные операции текущей транзакции определенно будут иметь другой доступ к транзакциям, поэтому пессимистически укажите, что эксклюзивная блокировка используется в приложении для блокировки ресурсов данных. Поддержите следующие формы в MySQL и Oracle:
Выберите ... для обновления
Явно позвольте выбрать Exclusive Lock Lock, чтобы заблокировать записи запроса. Для других транзакций запросить, обновлять или удалять эти заблокированные данные, они должны ждать, пока транзакция не закончится.
В Hibernate вы можете пройти в LockMode.upgrade при загрузке для принятия пессимистической блокировки. Изменить предыдущий пример,
При вызовах метода получения транзакций 1 и 2 передается дополнительный параметр блокировки. Как видно из журнала, транзакции 1 и 2
Он больше не является перекрестным, транзакция 2 может ждать, пока транзакция 1 завершится, прежде чем читать данные, поэтому конечное значение col_balance является правильным 100.
пакет com.cdai.orm.hibernate.transaction; Импорт org.hibernate.lockmode; Импорт org.hibernate.session; Импорт org.hibernate.sessionFactory; Импорт org.hibernate.transaction; Импорт com.cdai.orm.hibernate.annotation.account; импорт com.cdai.orm.hibernate.annotation.annotationhibernate; public Class upgradeLock {@suppresswarnings ("omemercation") public static void main (string [] args) {final sessionFactory sessionFactory = annotationBhiberNate.createsessionFactory (); // Запуск транзакции 1 поток t1 = new Thread () {@Override public void run () {session1 = sessionFactory.opensession (); Транзакция tx1 = null; try {tx1 = session1.begintransaction (); System.out.println ("T1 - Begin Trasaction"); Thread.sleep (500); Учетная запись = (учетная запись) session1.get (account.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 - изменение баланса:" + account.getBalance ()); tx1.commit (); System.out.println ("T1 - Compet Transaction"); Thread.sleep (500); } catch (Exception e) {e.printstackTrace (); if (tx1! = null) tx1.rollback (); } наконец {session1.close (); }}}; // запустить транзакцию 2 поток t2 = new Thread () {@Override public void run () {session2 = sessionFactory.opensession (); Транзакция tx2 = null; try {tx2 = session2.begintransaction (); System.out.println ("T2 - Begin Trasaction"); Thread.sleep (500); Учетная запись = (учетная запись) session2.get (account.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 - изменение баланса:" + account.getBalance ()); tx2.commit (); System.out.println ("T2 - Compet Transaction"); Thread.sleep (500); } catch (Exception e) {e.printstackTrace (); if (tx2! = null) tx2.rollback (); } наконец {session2.close (); }}}}; t1.start (); t2.start (); while (t1.isalive () || t2.isalive ()) {try {thread.sleep (2000l); } catch (прерывание Exception e) {}} System.out.println ("как T1, так и T2 мертвы."); sessionFactory.Close (); }}T1 - Begin Trasactiont2 - Begin TrasactionHibernate: SELECT Account0_.COL_ID AS COL1_0_0_, Account0_.COL_BALANCE AS COL2_0_0_ из TB_ACCOUNT Account0_ с (updlock, rowlock), где account0_.col_id =? Hibernate: Select Account0_.Col_id ASCLARIASE ASCOLERANCE_. COL2_0_0_ из TB_ACCOUNT Account0_ с (updlock, rowlock), где account0_.col_id =? T2 - Баланс = 100t2 - Изменить баланс: 0HIBERNATE: Обновление TB_ACCOUNT SET COL_BALANCE =? где col_id =? T2 - Commit Transactiont1 - Balance = 0T1 - Изменение Баланс: 100 Гибибернат: обновление TB_ACCOUNT SET COL_BALANCE =? где col_id =? T1 - Commit Transaction Both T1 и T2 мертвы.
Hibernate выполняет SQL для SQLServer 2005:
Кода -копия выглядит следующим образом:
Выберите account0_.col_id as col1_0_0_, account0_.col_balance как col2_0_0_ из TB_ACCOUNT Account0_ с (updlock, rowlock), где account0_.col_id =?
2. Оптимистическая блокировка: Предположим, что данные о текущей операции транзакции не будут доступны одновременно другими транзакциями, поэтому уровень изоляции базы данных полностью полагается на базу данных для автоматического управления работой блокировки. Принять контроль версий в приложениях, чтобы избежать проблем с параллелизмом, которые могут возникнуть с низкой вероятностью.
В Hibernate используйте аннотации версий, чтобы определить поле номера версий.
Замените объект учетной записи в Dirtylock на Accountversion, а другой код остается неизменным, и происходит исключение, когда происходит выполнение.
пакет com.cdai.orm.hibernate.transaction; Импорт javax.persistence.column; Импорт javax.persistence.entity; Импорт javax.persistence.id; Импорт javax.persistence.table; Импорт javax.persistence.version; @Entity @Table (name = "tb_account_version") public class accountversion {@id @column (name = "col_id") private long id; @Column (name = "col_balance") частный длинный баланс; @Version @column (name = "col_version") частная версия Int; public accountversion () {} public accountversion (long id, long balance) {this.id = id; this.balance = баланс; } public long getId () {return id; } public void setId (long id) {this.id = id; } public long getBalance () {return Balance; } public void setBalance (длинный баланс) {this.balance = balance; } public int getVersion () {return version; } public void setVersion (int version) {this.version = версия; }}Журнал заключается в следующем:
T1 - Begin Trasactiont2 - Begin TrasactionHibernate: SELECT AccountVer0_.COL_ID AS COL1_0_0_, accountver0_.col_balance as Col2_0_0_, accountver0_.col_version as col3_0_0_ от chybrence account_ accouscever0_, где accountver0_.col_id =? COL1_0_0_, accountver0_.col_balance as col2_0_0_, accountver0_.col_version as col3_0_0_ от tb_account_version accountver0_ где accountver0_.col_id =? T1 - Баланс = 1000t2 - Баланс = 1000t1 - изменение баланса: 900T2 - изменение баланса: col_balance =?, col_version =? где col_id =? и col_version =? hibernate: обновление tb_account_version set col_balance =?, col_version =? где col_id =? и col_version =? T1 - Commit Transaction2264 [Thread -2] Ошибка org.hibernate.event.def.abstractflushingeventListener - не мог синхронизировать состояние базы данных с помощью сеансаорга. [com.cdai.orm.hibernate.transaction.accountversion#1] at org.hibernate.persister.entity.abstractentitypersister.check (AbstractentityPersister.java:1934) at org.hibernate.persister.entity.abstractentitypersister.Update (AbstrletentTypers.javister. org.hibernate.persister.entity.abstractentitypersister.updateorinsert (AbstractentityPersister.java:2478) в org.hibernate.persister.entity.abstractentitypersister.Update (AbstractentityPersister.java:2805) в org.hibernate.action.entityupdateaction.execute (entityupdateaction.java:114) в org.hibernate.engine.actionqueue.execute (actionqueue.java:268) at org.hibernate.engine.actionqueue.ececuteactions (actionqueue.java:260). org.hibernate.engine.actionqueue.executeActions (actionqueue.java:180) в org.hibernate.event.def.abstractflushingeventlistener.performexection org.hibernate.event.def.defaultflusheventlisterener.onflush (defaultflusheventlistener.java:51) в org.hibernate.impl.sessionImpl.flush (sessionImpl.java:1206) на org.hibernate.impl.sessionImplush (sessionImpl.java org.hibernate.transaction.jdbctransaction.commit (jdbctransaction.java:137) на com.cdai.orm.hibernate.transaction.versionlock $ 2.run (версия залок.java:93) как T1, так и T2 мертвы.
Поскольку оптимистичная блокировка полностью оставляет выделение транзакции в базу данных для управления, транзакции 1 и 2 запускают перекрестный запуск, транзакция 1 успешно совершается, и COL_VERSION изменяется на 1. Однако, когда транзакция 2 коммиты, данные с COL_Version 0 больше не могут быть обнаружены, поэтому было добавлено исключение.
Сравнение методов гибернатного запроса
Есть три основных метода запроса для Hibernate:
1.HQL (язык сгибания).
Он очень похож на SQL и поддерживает такие функции, как пейджинг, подключение, группировка, функции агрегации и подзадность.
Но HQL ориентирован на объект, а не таблицы в реляционных базах данных. Поскольку операторы запроса ориентированы на объекты домена, использование HQL может получить кроссплатформенные преимущества. С зимний
Это автоматически поможет нам перевести в различные операторы SQL в соответствии с различными базами данных. Это очень удобно в приложениях, которые необходимо поддерживать несколько баз данных или миграции баз данных.
Но, получая его удобное, поскольку операторы SQL автоматически генерируются Hibernate, это не способствует оптимизации эффективности и отладке операторов SQL. Когда объем данных велик, могут быть проблемы с эффективностью.
Если есть проблема, это не удобно исследовать и разрешить ее.
2.QBC/QBE (запрос по критериям/примеру)
QBC/QBE выполняет запрос путем сборки условий запроса или шаблонов. Это удобно в приложениях, которые требуют гибкой поддержки для многих свободных комбинаций условий запроса. Такая же проблема заключается в том, что, поскольку оператор запроса свободно собирается, код для создания оператора может быть длинным и содержит много условий ветвления, что очень неудобно для оптимизации и отладки.
3. SQL
Hibernate также поддерживает методы запросов, которые непосредственно выполняют SQL. Этот метод приносит жертву преимуществам сгибальной перекрестной датабазы и вручную записывает основные операторы SQL для достижения наилучшей эффективности выполнения.
По сравнению с первыми двумя методами оптимизация и отладка более удобны.
Давайте посмотрим на набор простых примеров.
пакет com.cdai.orm.hibernate.query; импортировать java.util.arrays; импортировать java.util.list; Импорт org.hibernate.criteria; Импорт org.hibernate.query; Импорт org.hibernate.session; Импорт org.hibernate.sessionFactory; Импорт org.hibernate.cfg.annotationConfiguration; Импорт org.hibernate.criterion.criterion; Импорт org.hibernate.criterion.example; Импорт org.hibernate.criterion.Expression; Импорт 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.bhint.nannotation"). AddannotatedClass (Account.Class). buildSessionFactory (); Session Session = sessionFactory.Opensession (); // 1.hql Query Query = session.createquery ("из учетной записи как a a.id =: id"); Query.SetLong ("id", 1); Список result = Query.list (); for (Object Row: result) {System.out.println (row); } // 2. Критерии критериев qbc = session.createcriteria (account.class); Criteria.Add (Express.EQ ("id", new Long (2))); result = criteria.list (); for (Object Row: result) {System.out.println (row); } // 3. qbe account Пример = new account (); Пример.setBalance (100); result = session.createcritia (account.class). add (example.create (пример)). список(); for (Object Row: result) {System.out.println (row); } // 4.sql Query = session.createSqlQuery ("Выберите Top 10 * из TB_ACCOUNT ORDER BY COL_ID DESC"); result = Query.list (); for (Object Row: result) {System.out.println (Arrays.toString ((Object []) row)); } session.close (); }}Hibernate: выберите account0_.col_id as col1_0_, account0_.col_balance как col2_0_ из tb_account account0_ где account0_.col_id =? this_.col_id =? account [id = 2, balance = 100] hibernate: select this_.col_id как col1_0_0_, this_.col_balance как col2_0_0_ из tb_account this_ где (this_.col_balance =? col_id desc [2, 100] [1, 100]
Из журнала вы можете четко увидеть контроль Hibernate над сгенерированными операторами SQL. Конкретный метод запроса, который нужно выбирать, зависит от конкретного приложения.