Spring @Transactional-изоляция, распространение


может кто-нибудь объяснить что изоляции & распространение параметры в @Transactional аннотация на примере реального мира. В принципе, когда и почему я должен выбрать, чтобы изменить их значения по умолчанию.

10 302

10 ответов:

хороший вопрос, хотя и не тривиальный, чтобы ответить.

распространение

определяет, как операции связаны друг с другом. Общие параметры

  • Required: код всегда будет выполняться в транзакции. Создайте новую транзакцию или повторно используйте ее, если она доступна.
  • Requires_new: код всегда будет выполняться в новой транзакции. Приостановить текущую транзакцию, если она существует.

изоляции

определяет контракт данных между операциями.

  • Read Uncommitted: допускает "грязное" чтение
  • Read Committed: не допускает "грязное" чтение
  • Repeatable Read: если строка читается дважды в одной и той же транзакции, результат всегда будет одинаковым
  • Serializable: выполняет все транзакции в последовательности

разные уровни имеют разные характеристики производительности в многопоточном приложении. Я думаю, если вы понимаете сделки остается открытым если транзакция уже была открыта при входе в процедуру. Обратите внимание также, что результат 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 мы ожидаем, что все было откатано и резервное хранилище без изменений.

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; ни один из методов не выполняется в контексте транзакции.

уровень изоляции: Речь идет о том, насколько транзакция может быть затронута действиями других параллельных transactions.It A поддерживает согласованность, оставляя данные во многих таблицах в согласованном состоянии. Он включает в себя блокировку строк и/или таблиц в базе данных.

проблема с несколькими транзакциями

Сценарий 1.Если транзакция T1 считывает данные из таблицы A1, которая была записана другой параллельной транзакцией T2.Если на пути T2 происходит откат,то данные, полученные путем T1 является недействительным.Например, a=2-это исходные данные .Если T1 читать a=1, что было написано T2.Если откат T2, то a=1 будет откат до a=2 в ДБ.Но теперь T1 имеет a=1, но в ДБ таблица изменяется на a=2.

Scenario2.Если транзакция T1 считывает данные из таблицы А1.Если другая параллельная транзакция(T2) обновляет данные в таблице A1.Тогда данные, которые прочитал T1 отличается от таблицы А1.Потому что T2 обновил данные в таблице A1.Например, если T1 прочитал a=1 и T2 обновил a=2.Тогда а!=b.

Сценарий 3.Если транзакция T1 считывает данные из таблицы A1 с определенным количеством строк. Если другая параллельная транзакция (T2) вставляет больше строк в таблицу A1.Этот количество строк, прочитанных T1, отличается от строк в таблице А1

Сценарий 1 называется "грязное" чтение.

Сценарий 2 называется неповторяемые чтения.

Сценарий 3 называется Фантом читает.

Итак, уровень изоляции-это степень, до которой Сценарий 1, Сценарий 2, Сценарий 3 можно предотвратить. Вы можете получить полный уровень изоляции путем реализации блокировки.Это предотвращает одновременное чтение и пишет к таким же данным от происходить.Но это влиять на производительность. Уровень изоляции зависит от приложения к приложению, сколько изоляции требуемый.

ISOLATION_READ_UNCOMMITTED: позволяет читать изменения, которые еще не были committed.It страдают от сценария 1, Сценарий 2, Сценарий 3

ISOLATION_READ_COMMITTED: позволяет считывать из параллельных транзакций, которые были зафиксированы. Он может пострадать от сценария 2 и сценария 3. Потому что другие транзакции могут быть обновление данных.

ISOLATION_REPEATABLE_READ:многократное чтение одного и того же поля приведет к тем же результатам, пока оно не будет изменено itself.It может пострадать от сценария 3.Потому что другие транзакции могут вставлять данные

ISOLATION_SERIALIZABLE: Сценарий 1, Сценарий 2, Сценарий 3 никогда happens.It завершена isolation.It включает в себя полный locking.It аффеты performace из-за запирающий.

вы можете проверить с помощью

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 framework, мы также можем настроить, на каком уровне изоляции будет выполняться та же транзакция.

@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {

}

уровень изоляции READ_UNCOMMITTED указывает, что транзакция может считывать данные это все еще не зафиксировано другими транзакциями.

уровень изоляции READ_COMMITTED указывает, что транзакция не может считывать данные, которые еще не зафиксированы другими транзакциями.

уровень изоляции REPEATABLE_READ указывает, что если транзакция считывает одну запись из базы данных несколько раз, результат всех этих операций чтения всегда должен быть одинаковым.

сериализуемый уровень изоляции является наиболее ограничительным из всех уровней изоляции. Сделки выполняется с блокировкой на всех уровнях (блокировка чтения, диапазона и записи), поэтому они выглядят так, как будто они были выполнены сериализованным способом.

распространение это возможность решить, как бизнес-методы должны быть инкапсулированы в логических или физических транзакциях.

Spring REQUIRED behavior означает, что та же транзакция будет использоваться, если в текущем контексте выполнения метода bean уже открыта транзакция.

REQUIRES_NEW поведение означает, что контейнер всегда будет создавать новую физическую транзакцию.

вложенное поведение заставляет вложенные транзакции Spring использовать одну и ту же физическую транзакцию, но устанавливает точки сохранения между вложенными вызовами, поэтому внутренние транзакции могут также откатываться независимо от внешних транзакций.

обязательное поведение указывает, что существующая открытая транзакция уже должна существовать. Если не исключение будет брошено контейнером.

в Поведение NEVER указывает, что существующая открытая транзакция не должна уже существовать. Если транзакция существует, контейнер создает исключение.

поведение NOT_SUPPORTED будет выполняться вне области действия любой транзакции. Если открытая транзакция уже существует, она будет приостановлена.

поведение SUPPORTS будет выполняться в области транзакции, если открытая транзакция уже существует. Если нет открытой сделки метод выполнить в любом случае, но не транзакционным способом.

вы почти никогда не хотите использовать Read Uncommited Так как это не реально ACID совместимость. Read Commmited является хорошим стартовым местом по умолчанию. Repeatable Read вероятно, требуется только в сценариях отчетов, свертки или агрегации. Обратите внимание, что многие БД, включая postgres, на самом деле не поддерживают повторяемое чтение, вы должны использовать . Serializable полезно для вещей, которые, как вы знаете, должны происходить совершенно независимо от чего-либо еще; думайте об этом как synchronized в Java. Сериализуемый идет рука об руку с REQUIRES_NEW распространения.

я использую REQUIRES для всех функций, которые запускают обновление или удаление запросов, а также функции уровня" сервис". Для функций уровня DAO, которые только запускают SELECTs, я использую 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()) {
            }
        }
    }
    
      • outerMethod-без транзакции
      • method_1-распространение.Обязательно) -
      • method_2 - Только аннотация транзакции
      • вывод: method_1 выдаст исключение, что нет существующей транзакции
      • outerMethod-без транзакции
      • method_1-только аннотация транзакции
      • method_2-распространение.Обязательно)
      • вывод: method_2 выдаст исключение, что нет существующей транзакции
      • выход: method_1 сохранит запись внутри база данных.
      • outerMethod-с транзакцией
      • method_1-только аннотация транзакции
      • method_2-распространение.Обязательно)
      • вывод: method_2 сохранит запись в базе данных.
      • вывод: method_1 сохранит запись в базе данных. -- Здесь основная внешняя существующая транзакция используется как для метода 1, так и для метода 2
      • outerMethod - с транзакция
      • method_1-распространение.Обязательно) -
      • method_2-только аннотация транзакции и выбрасывает исключение
      • вывод: нет записи сохраняются в базе данных означает откат сделано.
      • outerMethod-с транзакцией
      • method_1-распространение.REQUIRES_NEW)
      • method_2-распространение.REQUIRES_NEW) и бросает 1/0 исключение
      • выход: method_2 будет бросает исключение так method_2 запись не сохраняется.
      • вывод: method_1 сохранит запись в базе данных.
      • вывод: нет отката для method_1

мы можем добавить к этому:

@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
    }
}

транзакция представляет собой единицу работы с базой данных.

весной TransactionDefinition интерфейс, определяющий свойства транзакции, совместимые с 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 inter thread communication)

+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| 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.                                 |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+

изоляции : изоляция является одним из кислотных (атомарность, согласованность, изоляция, долговечность) свойств транзакций базы данных. Изоляция определяет, как целостность транзакций видна другим пользователям и системам. Он использует для блокировки ресурсов, т. е. управления параллелизмом, убедитесь, что только одна транзакция может получить доступ к ресурсу в данной точке.

блокировка восприятия: уровень изоляции определяет длительность удержания блокировок.

+---------------------------+-------------------+-------------+-------------+------------------------+
| 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 вида основных проблем:

  • "грязное" чтение : считывает незафиксированные данные из другого tx (транзакции).
  • не повторяется читает: читает committed UPDATES из другого tx.
  • Фантом читает: читает committed 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       |
+---------------------------+----------------+----------------------+----------------+

примеры

Вы можете использовать такой:

@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();
}