transactional java что это
Изоляция и распространение транзакций в Spring
В статье, посвящённой декларативному управлению транзакциями в Spring я обещал отдельно описать изоляцию транзакций друг от друга и их распространение. Это время пришло.
Изоляция транзакций
Я уже писал про побочные эффекты, вызываемые параллельным исполнением запросов. Уровни изоляции транзакций можно рассматривать как механизм, позволяющий решать проблему параллельного доступа к данным и изменения данных без явных ручных блокировок.
Существует четыре уровня изоляции транзакций, в которых подобные побочные эффекты могут происходить, а могут и не происходить:
Распространение транзакций
Когда вызывается метод с @Transactional происходит особая уличная магия: proxy, который создал Spring, создаёт persistence context (или соединение с базой), открывает в нём транзакцию и сохраняет всё это в контексте нити исполнения (натурально, в ThreadLocal ). По мере надобности всё сохранённое достаётся и внедряется в бины. Привязка транзакций к нитям (threads) позволяет использовать семантику серверов приложений J2EE, в которой гарантируется, что каждый запрос получает свою собственную нить.
Все эти правила действуют как при вызове метода в текущем потоке, так и выполнения в другом потоке. В случае другого потока транзакция будет относится к нему.
Куда же ставить @Transactional?
Классическое приложение обычно имеет многослойную архитектуру:
контроллеры > слой логики > слой доступа к данным > слой ORM
Слой логики представляется идеальным местом для @Transactional : именно здесь набор запросов к базе оформляется в единую осмысленную операцию в приложении. Зная, что делает ваше приложение, вы можете чётко разграничить логические единицы работы в нём и расставить границы транзакций.
Как Spring @Transactional действительно работает?
В этом посте мы углубимся в управление транзакциями Spring. Мы рассмотрим, как @Transactional действительно работает под капотом. Другие предстоящие сообщения будут включать в себя:
JPA и управление транзакциями
Важно отметить, что JPA сама по себе не обеспечивает какого-либо декларативного управления транзакциями. При использовании JPA вне контейнера внедрения зависимостей, транзакции должны обрабатываться разработчиком программно:
Этот способ управления транзакциями делает область транзакции очень ясной в коде, но имеет несколько недостатков:
Использование Spring @Transactional
В Spring @Transactional приведенный выше код сводится к следующему:
Это намного удобнее и удобочитаемее, и в настоящее время это рекомендуемый способ обработки транзакций в Spring.
Одним из потенциальных недостатков является то, что этот мощный механизм скрывает то, что происходит под капотом, затрудняя отладку, когда что-то не работает.
Одним из ключевых моментов в @Transactional является то, что нужно рассмотреть две отдельные концепции, каждая со своей областью действия и жизненным циклом:
Контекст постоянства — это просто объект синхронизатора, который отслеживает состояние ограниченного набора объектов Java и гарантирует, что изменения в этих объектах в конечном итоге сохраняются обратно в базу данных.
Когда EntityManager охватывает несколько транзакций базы данных?
В таком случае запросы, которые выполняются на уровне представления, находятся в отдельных транзакциях базы данных, чем те, которые используются для бизнес-логики, но они выполняются через один и тот же менеджер сущностей.
Что определяет отношение EntityManager против транзакции?
Это на самом деле выбор разработчика приложения, но наиболее распространенный способ использования JPA Entity Manager — это шаблон «Entity Manager для каждой транзакции приложения». Это наиболее распространенный способ внедрения менеджера сущностей:
Введение в транзакции в Java и Spring
Краткое и практическое руководство по транзакциям в Java и Spring.
1. введение
В этом уроке мы поймем, что подразумевается под транзакциями в Java. Таким образом, мы поймем, как выполнять локальные транзакции ресурсов и глобальные транзакции. Это также позволит нам изучить различные способы управления транзакциями в Java и Spring.
2. Что такое Транзакция?
Кроме того, эти транзакции могут включать один или несколько ресурсов, таких как база данных, очередь сообщений, что приводит к различным способам выполнения действий в рамках транзакции. Они включают выполнение локальных транзакций ресурсов с отдельными ресурсами. В качестве альтернативы в глобальной транзакции могут участвовать несколько ресурсов.
3. Локальные транзакции Ресурсов
В Java у нас есть несколько способов доступа к ресурсу, такому как база данных, и работы с ним. Следовательно, то, как мы работаем с транзакциями, также не одно и то же. В этом разделе мы узнаем, как мы можем использовать транзакции с некоторыми из этих библиотек в Java, которые довольно часто используются.
3.1. JDBC
Однако, если мы хотим объединить несколько операторов в одну транзакцию, этого также можно достичь:
3.2. JPA
Java Persistence API (JPA) – это спецификация в Java, которая может использоваться для преодоления разрыва между объектно-ориентированными моделями предметной области и системами реляционных баз данных|/. Таким образом, существует несколько реализаций JPA, доступных от третьих сторон, таких как Hibernate, EclipseLink и iBatis.
Давайте посмотрим, как мы можем создать EntityManager и определить границу транзакции вручную:
3.3. JMS
Давайте посмотрим, как мы можем создать транзакцию Сеанс для отправки нескольких сообщений в рамках транзакции:
4. Глобальные Транзакции
4.1. JTA
JTA определяет стандартные интерфейсы Java между менеджером транзакций и другими сторонами распределенной транзакции:
Давайте разберемся в некоторых ключевых интерфейсах, выделенных выше:
4.2. JTS
На высоком уровне он поддерживает API транзакций Java (JTA). Менеджер транзакций JTS предоставляет услуги по транзакциям сторонам, участвующим в распределенной транзакции:
Службы, которые JTS предоставляет приложению, в значительной степени прозрачны, и поэтому мы можем даже не заметить их в архитектуре приложения. Архитектура JTS построена вокруг сервера приложений, который абстрагирует всю семантику транзакций от прикладных программ.
5. Управление транзакциями JTA
5.1. JTA в сервере приложений
Кроме того, у нас есть выбор с точки зрения того, как мы хотим управлять границей транзакции в нашем приложении. Это приводит к двум типам транзакций на сервере приложений Java:
5.2. Автономный JTA
Теперь Atomikos предоставляет нам последний кусочек головоломки, чтобы собрать все вместе, экземпляр UserTransaction :
Теперь мы готовы создать приложение с распределенной транзакцией, охватывающей всю нашу базу данных и очередь сообщений:
6. Поддержка транзакций весной
Мы видели, что обработка транзакций – это довольно сложная задача, которая включает в себя множество шаблонного кодирования и конфигураций. Кроме того, каждый ресурс имеет свой собственный способ обработки локальных транзакций. В Java JTA абстрагирует нас от этих вариаций, но дополнительно привносит детали, зависящие от поставщика, и сложность сервера приложений.
Платформа Spring предоставляет нам гораздо более чистый способ обработки транзакций, как локальных, так и глобальных транзакций ресурсов в Java. Это вместе с другими преимуществами Spring создает убедительные аргументы в пользу использования Spring для обработки транзакций. Кроме того, довольно легко настроить и переключить диспетчер транзакций с помощью Spring, который может быть предоставлен сервером или автономным.
6.1. Конфигурации
Классы UserTransactionImp и UserTransactionManager предоставляются Atomikos здесь.
6.2. Управление транзакциями
Пройдя через все конфигурации в последнем разделе, мы должны чувствовать себя совершенно ошеломленными! В конце концов, мы можем даже усомниться в преимуществах использования Spring. Но помните, что вся эта конфигурация позволила нам абстрагироваться от большинства стандартных шаблонов, специфичных для поставщика, и наш фактический код приложения вообще не должен об этом знать.
Итак, теперь мы готовы изучить, как использовать транзакции весной, когда мы намерены обновить базу данных и опубликовать сообщения. Весна предоставляет нам два способа достичь этого с их собственными преимуществами на выбор. Давайте разберемся, как мы можем их использовать:
Приведенного выше простого кода достаточно, чтобы разрешить операцию сохранения в базе данных и операцию публикации в очереди сообщений в транзакции JTA.
7. Запоздалые мысли
Как мы уже видели, обработка транзакций, особенно тех, которые охватывают несколько ресурсов, сложна. Кроме того, транзакции по своей сути блокируются, что наносит ущерб задержке и пропускной способности приложения. Кроме того, тестирование и поддержка кода с распределенными транзакциями непросто, особенно если транзакция зависит от базового сервера приложений. Таким образом, в целом, лучше всего вообще избегать транзакций, если мы можем!
8. Заключение
Подводя итог, в этом уроке мы обсудили транзакции в контексте Java. Мы прошли через поддержку локальных транзакций отдельных ресурсов в Java для разных ресурсов. Мы также рассмотрели способы достижения глобальных транзакций на Java.
Далее мы рассмотрели различные способы управления глобальными транзакциями на Java. Кроме того, мы поняли, как Spring облегчает нам использование транзакций в Java.
Наконец, мы рассмотрели некоторые из лучших практик при работе с транзакциями на Java.
Распространение транзакций и изоляция весной @Transactional
Подробнее об условиях изоляции и распространения можно узнать в @Transactional
Распространение транзакций и изоляция весной @Transactional
1. Введение
В этом учебнике мы покроем @Transactional аннотация и ее изоляция и распространение Параметры.
2. Что такое @Transactional?
Мы можем использовать @Transactional обернуть метод в транзакцию базы данных.
Это позволяет нам устанавливать условия распространения, изоляции, тайм-аута, только для чтения и отката для нашей транзакции. Также можно указать менеджера транзакций.
2.1. @Transactional подробности осуществления
Spring создает прокси или манипулирует кодом класса для управления созданием, коммитом и откатом транзакции. В случае прокси, Весна игнорирует @Transactional во внутренних вызовов метода.
Проще говоря, если у нас есть метод, как callMethod и мы отмечаем его как @Transactional, Весна обернет некоторый код управления транзакцией вокруг призыва: @Transactional метод под названием:
2.2. Как использовать @Transactional
Мы можем поместить аннотацию на определения интерфейсов, классов или непосредственно на методы. Они переопределяют друг друга в соответствии с приоритетом порядка; от самого низкого до самого высокого у нас есть: интерфейс, суперкласс, класс, метод интерфейса, метод суперкласса и метод класса.
Однако, если мы ставим аннотацию на частный или защищенный метод, Spring проигнорирует ее без ошибки.
Начнем с образца интерфейса:
Как правило, не рекомендуется устанавливать @Transactional на интерфейсе. Тем не менее, это приемлемо для таких случаев, как @Repository с весенними данными.
Мы можем поместить аннотацию в определение класса, чтобы переопределить параметр транзакции интерфейса/суперкласса:
Теперь давайте переопределим его, установив аннотацию непосредственно на методе:
3. Распространение транзакций
Распространение определяет границу транзакций нашей бизнес-логики. Spring удается начать и приостановить транзакцию в соответствии с нашими распространение оправа.
Теперь давайте рассмотрим различные распространения и как они работают.
3.1. РАСПРОСТРАНЕНИЕ REQUIRED
ОБЯЗАТЕЛЬНЫе является распространением по умолчанию. Весенние проверки, если есть активная транзакция, то она создает новую, если ничего не существовало. В противном случае бизнес-логика придатки к в настоящее время активной транзакции:
Кроме того, ОБЯЗАТЕЛЬНЫе является распространением по умолчанию, мы можем упростить код, сбросив его:
Давайте посмотрим псевдо-код того, как создание транзакций работает для ОБЯЗАТЕЛЬНЫе распространение:
3.2. Пропаганда SUPPORTS
Давайте посмотрим псевдокодей создания транзакции для SUPPORTS :
3.3. Распространение MANDATORY
И давайте еще раз посмотрим псевдо-код:
3.4. НИКОГДА не распространяется
Для транзакционной логики с НИКОГДА распространение, Весна бросает исключение, если есть активная транзакция:
Давайте посмотрим псевдо-код того, как создание транзакций работает для НИКОГДА распространение:
3.5. NOT_SUPPORTED Распространение
Весна сначала приостанавливает текущую транзакцию, если она существует, затем бизнес-логика выполняется без транзакции.
тем JTAТрансакцияМенагер поддерживает реальную приостановку транзакций из коробки. Другие моделируют подвеску, удерживая ссылку на существующую, а затем очищая ее из контекста потока
3.6. REQUIRES_NEW Распространение
И псевдо-код выглядит так:
3.7. Распространение NESTED
ДанныеИсточникТрансакцияМенагер поддерживает это распространение из коробки. Кроме того, некоторые реализации JTAТрансакцияМенагер может поддержать это.
Наконец, давайте распространение НЕ ВЛОЖЕННЫЕ :
4. Изоляция транзакций
Изоляция является одним из общих свойств ACID: атомачность, последовательность, изоляция и долговечность. Изоляция описывает, как изменения, применяемые параллельными транзакциями, видны друг другу.
Каждый уровень изоляции предотвращает нулевые или более побочные эффекты транзакции:
4.1. Управление изоляцией весной
Мы должны также рассмотреть случаи, когда мы называем цепочку методов с различной изоляцией . В обычном потоке изоляция применяется только при новой транзакции. Таким образом, если по какой-либо причине мы не хотим, чтобы метод для выполнения в другой изоляции, мы должны установить TransactionManager:setValidateExistingTransaction к истине. Тогда псевдо-код проверки транзакций будет:
Теперь давайте глубоко в различных уровнях изоляции и их последствия.
4.2. READ_UNCOMMITTED Изоляция
READ_UNCOMMITTED является самым низким уровнем изоляции и обеспечивает наиболее параллельный доступ.
В результате, он страдает от всех трех упомянутых побочных эффектов concurrency. Таким образом, транзакция с этой изоляцией считывает незавершенные данные других параллельных транзакций. Кроме того, как не повторяемые, так и фантомные чтения могут произойти. Таким образом, мы можем получить другой результат при повторном считыив строку или повторное выполнение запроса диапазона.
Мы можем установить изоляция уровень для метода или класса:
Postgres не поддерживает READ_UNCOMMITTED изоляции и падает обратно в READ_COMMITED место . Кроме того, Oracle не поддерживает и не READ_UNCOMMITTED .
4.3. READ_COMMITTED Изоляция
Второй уровень изоляции, READ_COMMITTED, предотвращает грязные читает.
Остальные побочные эффекты concurrency все еще могут случиться. Таким образом, незавершенные изменения в параллельных транзакциях не влияют на нас, но если транзакция совершает свои изменения, наш результат может измениться путем повторного запроса.
Здесь мы устанавливаем изоляция уровень:
READ_COMMITTED является уровнем по умолчанию с Postgres, сервером S’L и Oracle.
4.4. REPEATABLE_READ Изоляция
Третий уровень изоляции, REPEATABLE_READ, предотвращает грязные и не повторяемые чтения. Таким образом, на нас не влияют незавершенные изменения в параллельных транзакциях.
Кроме того, когда мы повторно запрос на строку, мы не получаем другой результат. Но при повторном выполнении диапазон-запросов мы можем получить недавно добавленные или удаленные строки.
Кроме того, это самый низкий требуемый уровень для предотвращения потерянного обновления. Потерянное обновление происходит, когда две или более одновременных транзакций считыв и обновляют один и тот же ряд. REPEATABLE_READ вообще не допускает одновременного доступа к строке. Таким образом, потерянное обновление не может произойти.
Вот как установить изоляция уровень для метода:
REPEATABLE_READ является уровень по умолчанию в Mysql. Oracle не поддерживает REPEATABLE_READ .
4.5. СЕРИАЛИЗИРУЕМАЯ изоляция
СЕРИАЛИЗИРУЕМЫЕ является наивысшим уровнем изоляции. Это предотвращает все упомянутые побочные эффекты concurrency, но может привести к самой низкой скорости одновременного доступа, поскольку он выполняет параллельные вызовы последовательно.
Другими словами, одновременное выполнение группы серийных транзакций имеет тот же результат, что и их выполнение в последовательном.
Теперь давайте посмотрим, как установить СЕРИАЛИЗИРУЕМЫЕ в качестве изоляция уровень:
5. Заключение
В этом учебнике мы исследовали распространение собственности @Transaction обстоятельно. После этого мы узнали о побочных эффектах с эквивалентности и уровнях изоляции.
Эффективное управление транзакциями в Spring
Что ж, конец месяца у нас всегда интенсивные, вот и тут остался всего день до старта второго потока курса «Разработчик на Spring Framework» — замечательного и интересного курса, который ведёт не менее прекрасный и злой Юрий (как его называют некоторые студент за уровень требований в ДЗ), так что давайте рассмотрим ещё один материал, который мы подготовили для вас.
Большую часть времени разработчики не придают значения управлению транзакциями. В результате либо большую часть кода приходится переписывать позже, либо разработчик реализует управление транзакциями без знаний того, как оно на самом деле должно работать или какие аспекты необходимо использовать конкретно в их случае.
Важный аспект в управлении транзакциями — определение правильных границы транзакции, когда транзакция должна начинаться и когда заканчиваться, когда данные должны быть добавлены в БД и когда они должны быть откачены обратно (в случае возникновения исключения).
Самый важный аспект для разработчиков — это понять как реализовать управление транзакциями в приложении наилучшим образом. Поэтому давайте рассмотрим различные варианты.
Способы управления транзакциями
Транзакции могут управляться следующими способами:
1. Программное управление путем написания пользовательского кода
Это старый способ управления транзакциями.
Spring поддерживает два типа управления транзакциями
1. Программное управление транзакциями: Вы должны управлять транзакциями с помощью программирования. Это способ достаточно гибкий, но его сложно поддерживать.
2. Декларативное управление транзакциями: Вы отделяете управление транзакциями от бизнес-логики. Вы используете только аннотации в конфигурации на основе XML для управления транзакциями.
Мы настоятельно рекомендуем использовать декларативные транзакции. Если вы хотите узнать причины, тогда читайте дальше, иначе переходите сразу к разделу Декларативное управление транзакциями, если хотите реализовать этот вариант.
Теперь давайте рассмотрим каждый подход детально.
2.1. Программное управление транзакциями:
Фреймворк Spring предоставляет два средства для программного управления транзакциями.
a. Использование TransactionTemplate (рекомендовано командой Spring):
Давайте рассмотрим как можно реализовать этот тип на примере кода, представленного ниже (взято из документации Spring с некоторыми изменениями)
Обратите внимание, что фрагменты кода взяты из Spring Docs.
Если нет возвращаемого значения, используйте удобный класс TransactionCallbackWithoutResult с анонимным классом, как показано ниже:
Давайте снова посмотрим на эту опцию в коде.
Теперь, перед тем как переходить к следующему способу управления транзакциями, давайте посмотрим как определиться, какой из типов управления транзакциями выбрать.
Выбор между Программным и Декларативным управлением транзакциями:
Шаг 1: Определите менеджер транзакций в контекстном xml файле вашего spring-приложения.
Шаг 2: Включите поддержку аннотаций, добавив запись в контекстном xml файле вашего spring-приложения.
ИЛИ добавьте @EnableTransactionManagement в ваш конфигурационный файл, как показано ниже:
Spring рекомендует аннотировать только конкретные классы (и методы конкретных классов) с аннотацией @Transactional в сравнении с аннотирующими интерфейсами.
Причина этого заключается в том, что вы помещаете аннотацию на уровень интерфейса, и если вы используете прокси-классы ( proxy-target-class = «true» ) или сплетающий аспект ( mode = «aspectj» ), тогда параметры транзакции не распознаются инфраструктурой проксирования и сплетения, например Транзакционное поведение не будет применяться.
Шаг 3: Добавьте аннотацию @Transactional в класс (метод класса) или интерфейс (метод интерфейса).
Конфигурация по умолчанию: proxy-target-class=»false»
READ_COMMITTED (чтение фиксированных данных): Постоянная, указывающая, что “грязное” чтение предотвращено; могут возникать неповторяющееся чтение и фантомное чтение.
READ_UNCOMMITTED (чтение незафиксированных данных): Этот уровень изоляции указывает, что транзакция может считывать данные, которые еще не удалены другими транзакциями.
REPEATABLE_READ (повторяемость чтения): Постоянная, указывающая на то, что “грязное” чтение и неповторяемое чтение предотвращаются; может появляться фантомное чтение.
SERIALIZABLE (упорядочиваемость): Постоянная, указывающая, что “грязное” чтение, неповторяемое чтение и фантомное чтение предотвращены.
Что означают эти жаргонизмы: “грязное” чтение, фантомное чтение или повторяемое чтение?
По умолчанию используется таймаут, установленный по умолчанию для базовой транзакционной системы.
Сообщает менеджеру tx о продолжительности времени, чтобы дождаться простоя tx, прежде чем принять решение об откате не отвечающих транзакций.
Указывает, что целевой метод не может работать без активного tx. Если tx уже запущен до вызова этого метода, то он будет продолжаться в том же tx, или новый tx начнется вскоре после вызова этого метода.
Значение по умолчанию: rollbackFor=RunTimeException.class
В Spring все классы API бросают RuntimeException, это означает, что если какой-либо метод не выполняется, контейнер всегда откатывает текущую транзакцию.
Указывает, что откат не должен происходить, если целевой метод вызывает это исключение.
Теперь последним, но самым важным шагом в управлении транзакциями является размещение аннотации @Transactiona l. В большинстве случаев возникает путаница, где должна размещаться аннотация: на сервисном уровне или на уровне DAO?
@Transactional : Сервисный или DAO уровень?
Существует много CRUD-приложений, у которых нет существенной бизнес-логики, имеющих сервисный уровень, который просто передает данные между контроллерами и объектами доступа к данным, что не является полезным. В этих случаях мы можем поместить аннотацию транзакции на уровень DAO.
Поэтому на практике вы можете поместить их в любом месте, это зависит от вас.
Кроме того, если вы поместите @Transactional в уровень DAO и если ваш уровень DAO будет повторно использоваться разными службами, тогда будет сложно разместить его на уровне DAO, так как разные службы могут иметь разные требования.
Если ваш сервисный уровень извлекает объекты с помощью Hibernate, и, допустим, у вас есть ленивые инициализации в определении объекта домена, тогда вам нужно открыть транзакцию на сервисном уровне, иначе вам придется столкнуться с LazyInitializationException, брошенным ORM.
Рассмотрим другой пример, когда ваш уровень обслуживания может вызывать два разных метода DAO для выполнения операций БД. Если ваша первая операция DAO завершилась неудачей, остальные две могут быть переданы, и вы закончите несогласованное состояние БД. Аннотирование на сервисном уровне может спасти вас от таких ситуаций.
Надеюсь, эта статья вам помогла.
Всегда интересно увидеть ваш комментарии или вопросы.