Ответы:
Хороший вопрос, хотя и не тривиальный.
Определяет, как транзакции связаны друг с другом. Общие параметры:
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 никогда не происходит. Это полная изоляция. Она включает в себя полную блокировку. Она влияет на производительность из-за блокировки.
Вы можете проверить с помощью
public class TransactionBehaviour {
// set is either using xml Or annotation
DataSourceTransactionManager manager=new DataSourceTransactionManager();
SimpleTransactionStatus status=new SimpleTransactionStatus();
;
public void beginTransaction()
{
DefaultTransactionDefinition Def = new DefaultTransactionDefinition();
// overwrite default PROPAGATION_REQUIRED and ISOLATION_DEFAULT
// set is either using xml Or annotation
manager.setPropagationBehavior(XX);
manager.setIsolationLevelName(XX);
status = manager.getTransaction(Def);
}
public void commitTransaction()
{
if(status.isCompleted()){
manager.commit(status);
}
}
public void rollbackTransaction()
{
if(!status.isCompleted()){
manager.rollback(status);
}
}
Main method{
beginTransaction()
M1();
If error(){
rollbackTransaction()
}
commitTransaction();
}
}
Вы можете отлаживать и видеть результат с различными значениями для изоляции и распространения.
Достаточное объяснение каждого параметра дано другими ответами; Однако вы просили привести пример из реальной жизни, вот тот, который проясняет назначение различных вариантов распространения :
Предположим, вы отвечаете за внедрение службы регистрации, в которой пользователю отправляется электронное письмо с подтверждением. Вы получаете два сервисных объекта, один для регистрации пользователя и один для отправки электронной почты, который последний вызывается внутри первого. Например что-то вроде этого:/* Sign Up service */
@Service
@Transactional(Propagation=REQUIRED)
class SignUpService{
...
void SignUp(User user){
...
emailService.sendMail(User);
}
}
/* E-Mail Service */
@Service
@Transactional(Propagation=REQUIRES_NEW)
class EmailService{
...
void sendMail(User user){
try{
... // Trying to send the e-mail
}catch( Exception)
}
}
Возможно, вы заметили, что второй сервис имеет тип распространения REQUIRES_NEW и, более того, есть вероятность, что он выдает исключение (SMTP-сервер отключен, недействительная электронная почта или другие причины). Возможно, вы не хотите, чтобы весь процесс откатывался, например удаление информации о пользователе из базы данных или других вещей; поэтому вы вызываете вторую услугу в отдельной транзакции.
Возвращаясь к нашему примеру, на этот раз вы беспокоитесь о безопасности базы данных, поэтому вы определяете свои классы DAO следующим образом:/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
// some CRUD methods
}
Это означает, что всякий раз, когда создается объект DAO и, следовательно, возможный доступ к БД, мы должны заверить, что вызов был сделан из одной из наших служб, подразумевая, что должна существовать живая транзакция; в противном случае возникает исключение. Поэтому распространение имеет тип ОБЯЗАТЕЛЬНЫЙ .
Уровень изоляции определяет, как изменения, внесенные в один репозиторий данных одной транзакцией, влияют на другие одновременные параллельные транзакции, а также как и когда эти измененные данные становятся доступными для других транзакций. Когда мы определяем транзакцию с использованием среды Spring, мы также можем настроить, на каком уровне изоляции будет выполняться та же транзакция.
@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {
}
Уровень изоляции READ_UNCOMMITTED гласит, что транзакция может считывать данные, которые все еще не переданы другими транзакциями.
Уровень изоляции READ_COMMITTED гласит, что транзакция не может прочитать данные, которые еще не зафиксированы другими транзакциями.
Уровень изоляции REPEATABLE_READ гласит, что если транзакция считывает одну запись из базы данных несколько раз, результат всех этих операций чтения всегда должен быть одинаковым.
Сериализуемый уровень изоляции является наиболее ограничивающим из всех уровней изоляции. Транзакции выполняются с блокировкой на всех уровнях (чтение, диапазон и блокировка записи), поэтому они выглядят так, как если бы они выполнялись сериализованным способом.
Распространение - это способность решать, как бизнес-методы должны быть инкапсулированы как в логические, так и в физические транзакции.
Поведение Spring REQUIRED означает, что та же транзакция будет использоваться, если в текущем контексте выполнения метода компонента уже есть открытая транзакция.
Поведение REQUIRES_NEW означает, что контейнер всегда будет создавать новую физическую транзакцию.
Поведение NESTED заставляет вложенные транзакции Spring использовать ту же физическую транзакцию, но устанавливает точки сохранения между вложенными вызовами, поэтому внутренние транзакции могут также выполнять откат независимо от внешних транзакций.
Поведение ОБЯЗАТЕЛЬНО заявляет, что существующая открытая транзакция уже должна существовать. Если не исключение, будет выброшено контейнером.
В поведении НИКОГДА не говорится, что существующая открытая транзакция не должна существовать. Если транзакция существует, контейнер будет выдавать исключение.
Поведение NOT_SUPPORTED будет выполняться вне области действия любой транзакции. Если открытая транзакция уже существует, она будет приостановлена.
Поведение SUPPORTS будет выполняться в области транзакции, если открытая транзакция уже существует. Если нет уже открытой транзакции, метод все равно будет выполняться, но не транзакционным способом.
Транзакция представляет собой единицу работы с базой данных.
В TransactionDefinition
Spring интерфейс, который определяет Spring-совместимые свойства транзакции. @Transactional
аннотация описывает атрибуты транзакции для метода или класса.
@Autowired
private TestDAO testDAO;
@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {
// Interact with testDAO
}
Распространение (воспроизведение): используется для межтранзакционных отношений. (аналогично межпотоковой коммуникации Java)
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| value | Propagation | Description |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| -1 | TIMEOUT_DEFAULT | Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
| 0 | PROPAGATION_REQUIRED | Support a current transaction; create a new one if none exists. |
| 1 | PROPAGATION_SUPPORTS | Support a current transaction; execute non-transactionally if none exists. |
| 2 | PROPAGATION_MANDATORY | Support a current transaction; throw an exception if no current transaction exists. |
| 3 | PROPAGATION_REQUIRES_NEW | Create a new transaction, suspending the current transaction if one exists. |
| 4 | PROPAGATION_NOT_SUPPORTED | Do not support a current transaction; rather always execute non-transactionally. |
| 5 | PROPAGATION_NEVER | Do not support a current transaction; throw an exception if a current transaction exists. |
| 6 | PROPAGATION_NESTED | Execute within a nested transaction if a current transaction exists. |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
Изоляция. Изоляция является одним из свойств ACID (атомарность, согласованность, изоляция, долговечность) транзакций базы данных. Изоляция определяет, как целостность транзакции видна другим пользователям и системам. Он использует для блокировки ресурсов, то есть управления параллелизмом, убедитесь, что только одна транзакция может получить доступ к ресурсу в данной точке.
Восприятие блокировки: уровень изоляции определяет продолжительность удержания блокировки.
+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode | Read | Insert | Update | Lock Scope |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED | uncommitted data | Allowed | Allowed | No Lock |
| READ_COMMITTED (Default) | committed data | Allowed | Allowed | Lock on Committed data |
| REPEATABLE_READ | committed data | Allowed | Not Allowed | Lock on block of table |
| SERIALIZABLE | committed data | Not Allowed | Not Allowed | Lock on full table |
+---------------------------+-------------------+-------------+-------------+------------------------+
Читайте восприятие: возникают следующие 3 вида основных проблем:
UPDATES
с другого tx.INSERTS
и / или DELETES
с другого TXУровни изоляции с различными типами чтения:
+---------------------------+----------------+----------------------+----------------+
| Isolation Level Mode | Dirty reads | Non-repeatable reads | Phantoms reads |
+---------------------------+----------------+----------------------+----------------+
| READ_UNCOMMITTED | allows | allows | allows |
| READ_COMMITTED (Default) | prevents | allows | allows |
| REPEATABLE_READ | prevents | prevents | allows |
| SERIALIZABLE | prevents | prevents | prevents |
+---------------------------+----------------+----------------------+----------------+
Вы почти никогда не хотите использовать, 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. Обычно это реализуется клиентским менеджером транзакций / соединений.
Ссылка:
Я бежал outerMethod
, method_1
и method_2
с другим режимом распространения.
Ниже приведен вывод для другого режима распространения.
Внешний метод
@Transactional
@Override
public void outerMethod() {
customerProfileDAO.method_1();
iWorkflowDetailDao.method_2();
}
method_1
@Transactional(propagation=Propagation.MANDATORY)
public void method_1() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "XXX");
session.save(entity);
System.out.println("Method - 1 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
Method_2
@Transactional()
@Override
public void method_2() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "CCC");
session.save(entity);
int i = 1/0;
System.out.println("Method - 2 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
Мы можем добавить для этого:
@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {
public Customer getDetail(String customername) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateCustomer(Customer customer) {
// do something
}
}
Вы можете использовать так:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}
Вы можете использовать эту вещь также:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}