4.18.2013

Hibernate интересные моменты часть 2

Эту статью я бы хотел посвятить транзакциям и как они реализованы в Hibernate. Сначала давайте расмотрим какие интересные моменты могут случаться в "черном ящике" - Баз Данных (oracle,mysql,interbase,mssql ...):

  1. lost update -  может случиться когда две транзакции одновременно обновляют, выполняют операцию update над некоторой записью и 2 транзакция (самая последния транзакция по времени) заканчиваеться не удачно, она откатывает все изменения внесенные 1 и 2 транзакцией. Такая ситуация может случиться когда БД не использует блокировок (locking) и конкурирующие транзакции (concurrent transactions) в ней  не изолированны друг от друга.
  2. dirty read - может случиться когда 1-я транзакция изменяет строку, 2-я транзакция читает эту строку, 1-я откатывает изменения зделанные в транзакции. 2-я транзакция имеет не достоверные данные.
  3. unrepeatable read - может случиться когда 1-я транзакция дважды читает одну и ту же строку, и получает разные результаты, в промежутке между двумя чтениями 2-я транзакция изменяет ту же строку, и 1-я транзакция при повторном чтении получает новые данные. Так же частным случаем unrepeatable read является  проблема second lost updates problem. Представте себе что две конкурирующих транзакций одновременно читают одну и туже строчку, потом 1-я транзакция изменяет её и завершается удачно(commit), потом 2-я транзакция изменяет ту же строчку и перезаписывает данные, при этом теряются все изменения внесенные 1-й транзакцией.
  4. phantom read - может случиться когда одна и та же транзакция читает строки дважды через какой-то промежуток времени, и 2-я транзакция вставляет новую строчку или удаляет, до второго чтения.
И для предотвращения этих казусов в ANSI SQL стандарте предусмотрены standard isolation levels:
  1. read uncommitted transaction isolation - сдесь может случиться  dirty read,  но никогда не случиться lost update. Ни какая транзакция не может перезаписать не подтвержденные данные, достигается за счет блокировки (exclusive write locks), однако они могут быть прочтены любой транзакцией.
  2.  read committed transaction isolation - сдесь может случиться  unrepeatable read, но никогда не случиться dirty read. Транзакции чтения ни когда не блокируют читаемые строки, но при изменении данных, блокируются изменяемые строки.
  3. repeatable read transaction isolation - сдесь может случиться  phantom read, но никогда не случиться dirty read и unrepeatable read. Транзакции чтения блокируют данные строки. И в них не могут писать, ни какие данные транзакции, читать могут все. Транзакции записи блокирют все другие транзакции.
  4. serializable transaction isolation - представте очередь из транзакций и они выполняются одна, за другой по "очереди".
И так отсюда следует что, чем выше уровень транзакции тем не поворотнее становиться работа с базой данных, по этому нужно правильно выбрать устраивающий вас уровень, чем и займемся далее.Каждая база данных имеет по умолчанию transaction isolation level - обычно это read committed (чаще встречается) или repeatable read. Вы конечно можете изминить этот уровень в объекте java.sql.Connection драйвера JDBC:

1 - Read uncommitted isolation
2 - Read committed isolation
4 - Repeatable read isolation
8 - Serializable isolation

Или в Hibernate в параметре hibernate.connection.isolation проставить нужный вам уровень изоляции транзакции:

    <?xml version='1.0' encoding='utf-8'?>
    <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
        <session-factory>
            <!-- Database connection settings -->
            <property name="connection.driver_class">org.h2.Driver</property>
            <property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE</property>
            <property name="connection.username">sa</property>
            <property name="connection.password"></property>
    ...        
            <property name="connection.isolation">1</property>
            <!-- 1-Read uncommitted isolation
                 2-Read committed isolation
                 4-Repeatable read isolation
                 8-Serializable isolation   -->
    ...
        </session-factory>
    </hibernate-configuration> 
    
    при запуске приложения в логе hibernate  появятся такие строчки:
    17.04.2013 21:45:31 org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
    INFO: HHH000149: JDBC isolation level: READ_UNCOMMITTED
    
    PS.
    если вы используете пул подключения баз данных ч/з application server - то меняйте настройки у него (у сервера), параметр hibernate.connection.isolation не влияет на настройки сервера. Так же хочу сказать что у Hibernate реализованы 2 механизма контроля транзакций - это version checking (версионность - оптимистическая блокировка,  реализован специальный внутренний механизм в Hibernate) и pessimistic locking (песимистическая блокировка - реализовано блокировками  уровня баз данных). Надеюсь вскоре я напишу пару статей посвященных блокировкам в Hibernate.

    Комментариев нет:

    Отправить комментарий