Может кто-нибудь объяснить, для чего нужны параметры изоляции и распространения в @Transactional
аннотации на примере из реальной жизни?
В основном, когда и почему я должен выбрать, чтобы изменить их значения по умолчанию.
Может кто-нибудь объяснить, для чего нужны параметры изоляции и распространения в @Transactional
аннотации на примере из реальной жизни?
В основном, когда и почему я должен выбрать, чтобы изменить их значения по умолчанию.
Хороший вопрос, хотя и не тривиальный.
Определяет, как транзакции связаны друг с другом. Общие параметры:
Required
: Код всегда будет выполняться в транзакции. Создает новую транзакцию или использует ее повторно, если она доступна.Requires_new
: Код всегда будет выполняться в новой транзакции. Приостанавливает текущую транзакцию, если она существует.Определяет контракт данных между транзакциями.
Read Uncommitted
: Допускает грязное чтение.Read Committed
: Не допускает грязного чтения.Repeatable Read
: Если строка читается дважды в одной и той же транзакции, результат всегда будет одинаковым.Serializable
: Выполняет все транзакции в последовательности.Различные уровни имеют разные характеристики производительности в многопоточном приложении. Я думаю, что если вы понимаете dirty reads
концепцию, вы сможете выбрать хороший вариант.
Пример того, когда может произойти грязное чтение:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
Таким образом, может использоваться здравое значение по умолчанию (если таковое может быть заявлено) Read Committed
, которое позволяет только читать значения, которые уже были зафиксированы другими запущенными транзакциями, в сочетании с уровнем распространения Required
. Тогда вы можете работать оттуда, если у вашего приложения есть другие потребности.
Практический пример того, как новая транзакция всегда будет создаваться при вводе provideService
подпрограммы и завершаться при выходе:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
Если бы мы вместо этого использовали Required
, транзакция оставалась бы открытой, если транзакция была уже открыта при входе в подпрограмму. Также обратите внимание, что результат a rollback
может отличаться, так как в одной транзакции могут участвовать несколько исполнений.
Мы можем легко проверить поведение с помощью теста и увидеть, как результаты отличаются с уровнями распространения:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {
private @Autowired TransactionManager transactionManager;
private @Autowired FooService fooService;
@Test
public void testProvideService() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);
// assert repository values are unchanged ...
}
С уровнем распространения
Requires new
: Мы ожидаем , что fooService.provideService()
было НЕ откат , так как он создал свой собственный суб-транзакций.
Required
: мы ожидаем, что все было отменено, а резервный магазин не изменился.
sessionFactory.getCurrentTransaction()
добавления больше не нужно запускатьHibernateTemplate
управление транзакциями. Я удалил его :)PROPAGATION_REQUIRED = 0 ; Если DataSourceTransactionObject T1 уже запущен для метода M1. Если для другого метода M2 требуется объект транзакции, новый объект транзакции не создается. Один и тот же объект T1 используется для M2
PROPAGATION_MANDATORY = 2 ; метод должен выполняться внутри транзакции. Если ни одна из существующих транзакций не выполняется, будет сгенерировано исключение
PROPAGATION_REQUIRES_NEW = 3 ; Если DataSourceTransactionObject T1 уже запущен для метода M1 и он выполняется (выполняется метод M1). Если другой метод M2 начинает выполнение, то T1 приостанавливается на время метода M2 с новым DataSourceTransactionObject T2 для M2.M2, запущенного в его собственном контексте транзакции
PROPAGATION_NOT_SUPPORTED = 4 ; Если DataSourceTransactionObject T1 уже запущен для метода M1.Если одновременно выполняется другой метод M2, то M2 не должен выполняться в контексте транзакции. T1 приостанавливается до завершения M2.
PROPAGATION_NEVER = 5 ; Ни один из методов не выполняется в контексте транзакции.
Уровень изоляции: речь идет о том, насколько на транзакцию могут повлиять действия других параллельных транзакций. Это обеспечивает согласованность, оставляя данные во многих таблицах в согласованном состоянии. Это включает в себя блокировку строк и / или таблиц в базе данных.
Проблема с несколькими транзакциями
Сценарий 1. Если транзакция T1 считывает данные из таблицы A1, которые были записаны другой параллельной транзакцией T2. Если на пути T2 выполняется откат, то данные, полученные T1, являются недействительными. Например, a = 2 - исходные данные. Если T1 читает a = 1, который был записан T2.Если откат T2, тогда a = 1 будет откат до a = 2 в DB. Но, теперь, T1 имеет a = 1, но в таблице DB он изменяется на a = 2.
Сценарий 2. Если транзакция T1 считывает данные из таблицы A1.Если другая параллельная транзакция (T2) обновляет данные в таблице A1.Затем данные, которые T1 прочитал, отличаются от таблицы A1. Потому что T2 обновил данные в таблице A1.Eg, если T1 прочитайте a = 1 и T2 обновите a = 2. Затем a! = b.
Сценарий 3. Если транзакция T1 считывает данные из таблицы A1 с определенным количеством строк. Если другая параллельная транзакция (T2) вставляет больше строк в таблицу A1. Количество строк, считываемых T1, отличается от строк в таблице A1
Сценарий 1 называется « Грязное чтение».
Сценарий 2 называется Неповторяемое чтение.
Сценарий 3 называется Фантомное чтение.
Таким образом, уровень изоляции - это предел, до которого Сценарий 1, Сценарий 2, Сценарий 3 может быть предотвращен. Вы можете получить полный уровень изоляции, реализовав блокировку. Это предотвращает одновременное чтение и запись в одни и те же данные. Но это влияет на производительность. Уровень изоляции зависит от приложения к приложению, насколько требуется изоляция.
ISOLATION_READ_UNCOMMITTED : позволяет читать изменения, которые еще не были зафиксированы. Сценарий 1, сценарий 2, сценарий 3
ISOLATION_READ_COMMITTED : разрешает чтение из одновременных транзакций, которые были зафиксированы. Это может пострадать от сценария 2 и сценария 3. Потому что другие транзакции могут обновлять данные.
ISOLATION_REPEATABLE_READ : Многократное чтение одного и того же поля даст одинаковые результаты, пока оно не будет изменено само по себе. Сценарий 3 может пострадать. Поскольку другие транзакции могут вставлять данные
ISOLATION_SERIALIZABLE : Сценарий 1, Сценарий 2, Сценарий 3 никогда не происходит. Это полная изоляция. Она включает в себя полную блокировку. Она влияет на производительность из-за блокировки.
Вы можете проверить с помощью
Вы можете отлаживать и видеть результат с различными значениями для изоляции и распространения.
источник
Достаточное объяснение каждого параметра дано другими ответами; Однако вы просили привести пример из реальной жизни, вот тот, который проясняет назначение различных вариантов распространения :
Предположим, вы отвечаете за внедрение службы регистрации, в которой пользователю отправляется электронное письмо с подтверждением. Вы получаете два сервисных объекта, один для регистрации пользователя и один для отправки электронной почты, который последний вызывается внутри первого. Например что-то вроде этого:Возможно, вы заметили, что второй сервис имеет тип распространения REQUIRES_NEW и, более того, есть вероятность, что он выдает исключение (SMTP-сервер отключен, недействительная электронная почта или другие причины). Возможно, вы не хотите, чтобы весь процесс откатывался, например удаление информации о пользователе из базы данных или других вещей; поэтому вы вызываете вторую услугу в отдельной транзакции.
Возвращаясь к нашему примеру, на этот раз вы беспокоитесь о безопасности базы данных, поэтому вы определяете свои классы DAO следующим образом:Это означает, что всякий раз, когда создается объект DAO и, следовательно, возможный доступ к БД, мы должны заверить, что вызов был сделан из одной из наших служб, подразумевая, что должна существовать живая транзакция; в противном случае возникает исключение. Поэтому распространение имеет тип ОБЯЗАТЕЛЬНЫЙ .
источник
Уровень изоляции определяет, как изменения, внесенные в один репозиторий данных одной транзакцией, влияют на другие одновременные параллельные транзакции, а также как и когда эти измененные данные становятся доступными для других транзакций. Когда мы определяем транзакцию с использованием среды Spring, мы также можем настроить, на каком уровне изоляции будет выполняться та же транзакция.
Уровень изоляции READ_UNCOMMITTED гласит, что транзакция может считывать данные, которые все еще не переданы другими транзакциями.
Уровень изоляции READ_COMMITTED гласит, что транзакция не может прочитать данные, которые еще не зафиксированы другими транзакциями.
Уровень изоляции REPEATABLE_READ гласит, что если транзакция считывает одну запись из базы данных несколько раз, результат всех этих операций чтения всегда должен быть одинаковым.
Сериализуемый уровень изоляции является наиболее ограничивающим из всех уровней изоляции. Транзакции выполняются с блокировкой на всех уровнях (чтение, диапазон и блокировка записи), поэтому они выглядят так, как если бы они выполнялись сериализованным способом.
Распространение - это способность решать, как бизнес-методы должны быть инкапсулированы как в логические, так и в физические транзакции.
Поведение Spring REQUIRED означает, что та же транзакция будет использоваться, если в текущем контексте выполнения метода компонента уже есть открытая транзакция.
Поведение REQUIRES_NEW означает, что контейнер всегда будет создавать новую физическую транзакцию.
Поведение NESTED заставляет вложенные транзакции Spring использовать ту же физическую транзакцию, но устанавливает точки сохранения между вложенными вызовами, поэтому внутренние транзакции могут также выполнять откат независимо от внешних транзакций.
Поведение ОБЯЗАТЕЛЬНО заявляет, что существующая открытая транзакция уже должна существовать. Если не исключение, будет выброшено контейнером.
В поведении НИКОГДА не говорится, что существующая открытая транзакция не должна существовать. Если транзакция существует, контейнер будет выдавать исключение.
Поведение NOT_SUPPORTED будет выполняться вне области действия любой транзакции. Если открытая транзакция уже существует, она будет приостановлена.
Поведение SUPPORTS будет выполняться в области транзакции, если открытая транзакция уже существует. Если нет уже открытой транзакции, метод все равно будет выполняться, но не транзакционным способом.
источник
Транзакция представляет собой единицу работы с базой данных.
В
TransactionDefinition
Spring интерфейс, который определяет Spring-совместимые свойства транзакции.@Transactional
аннотация описывает атрибуты транзакции для метода или класса.Восприятие блокировки: уровень изоляции определяет продолжительность удержания блокировки.
Читайте восприятие: возникают следующие 3 вида основных проблем:
UPDATES
с другого tx.INSERTS
и / илиDELETES
с другого TXУровни изоляции с различными типами чтения:
Например
источник
Вы почти никогда не хотите использовать,
Read Uncommited
так как это не совсемACID
соответствует.Read Commmited
хорошее начальное место по умолчанию.Repeatable Read
вероятно, требуется только в отчетах, сценариях объединения или агрегирования. Обратите внимание, что многие БД, включая postgres, на самом деле не поддерживают Repeatable Read, вы должны использоватьSerializable
вместо этого.Serializable
полезен для вещей, которые, как вы знаете, должны происходить совершенно независимо от чего-либо еще; думать об этом, какsynchronized
в Java. Сериализуемый идет рука об руку сREQUIRES_NEW
распространением.Я использую
REQUIRES
все функции, выполняющие запросы UPDATE или DELETE, а также функции уровня «service». Для функций уровня DAO, которые запускают только SELECT, я использую,SUPPORTS
который будет участвовать в TX, если он уже запущен (т.е. вызывается из сервисной функции).источник
Изоляция транзакции и распространение транзакции, хотя и связаны между собой, но, безусловно, представляют собой две совершенно разные концепции. В обоих случаях значения по умолчанию настраиваются на пограничном компоненте клиента с помощью декларативного управления транзакциями или программного управления транзакциями . Детали каждого уровня изоляции и атрибутов распространения можно найти в ссылочных ссылках ниже.
Изоляция транзакции
Для заданных двух или более запущенных транзакций / подключений к базе данных, как и когда изменения, вносимые запросами в одной транзакции, влияют / видны для запросов в другой транзакции. Это также связано с тем, какой тип блокировки записей базы данных будет использоваться для изоляции изменений в этой транзакции от других транзакций и наоборот. Обычно это реализуется базой данных / ресурсом, который участвует в транзакции.
,
Распространение транзакции
В корпоративном приложении для любого запроса / обработки есть много компонентов, которые используются для выполнения работы. Некоторые из этих компонентов отмечают границы (начало / конец) транзакции, которая будет использоваться в соответствующем компоненте и его подкомпонентах. Для этой транзакционной границы компонентов Распространение транзакции указывает, будет ли соответствующий компонент участвовать или не будет участвовать в транзакции, и что произойдет, если вызывающий компонент уже имеет или не имеет транзакцию, уже созданную / запущенную. Это то же самое, что и атрибуты транзакции Java EE. Обычно это реализуется клиентским менеджером транзакций / соединений.
Ссылка:
Spring Transaction Management
Wiki Transaction Isolation (системы баз данных)
Oracle на уровне изоляции транзакций
Атрибуты транзакции Java EE (распространение)
Распространение транзакций в Spring Framework
источник
Я бежал
outerMethod
,method_1
иmethod_2
с другим режимом распространения.Ниже приведен вывод для другого режима распространения.
Внешний метод
method_1
Method_2
источник
Мы можем добавить для этого:
источник
Вы можете использовать так:
Вы можете использовать эту вещь также:
источник