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




spring transactional javadoc (7)

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


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

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

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

Я использую REQUIRES для всех функций, которые запускают запросы UPDATE или DELETE, а также функции уровня обслуживания. Для функций уровня DAO, которые запускают только SELECT, я использую SUPPORTS, которые будут участвовать в TX, если он уже запущен (т.е. вызван из сервисной функции).


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

Изоляция транзакций

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

,

Распространение транзакций

В корпоративном приложении для любого заданного запроса / обработки есть много компонентов, которые задействованы для выполнения работы. Некоторые из этих компонентов отмечают границы (начало / конец) транзакции, которые будут использоваться в соответствующем компоненте и его субкомпонентах. Для этой транзакционной границы компонентов Transaction Propogation указывает, будет ли соответствующий компонент участвовать или не участвовать в транзакции, и что произойдет, если вызывающий компонент уже имеет или не имеет уже созданную / запущенную транзакцию. Это то же самое, что и атрибуты транзакций Java EE. Обычно это выполняется менеджером транзакций / подключения клиента.

Ссылка:


Мы можем добавить для этого:

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

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

Propagation

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

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

Isolation

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

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

Практический пример, когда новая транзакция всегда будет создана при вводе подпрограммы обеспечения и завершена при выходе.

public class FooService {
    private Repository repo1;
    private Repository repo2;

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void provideService() {
        repo1.retrieveFoo();
        repo2.retrieveFoo();
    }
}

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


Я запускаю 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 - Только аннотация транзакции
      • Вывод: метод_1 будет генерировать исключение, если никакая существующая транзакция
      • outerMethod - Без транзакции
      • method_1 - Только аннотация транзакции
      • метод_2 - Распространение.МАНДАТОРИЯ)
      • Вывод: метод_2 будет генерировать исключение, если никакая существующая транзакция
      • Результат: метод_1 будет сохраняться в базе данных.
      • outerMethod - С транзакцией
      • method_1 - Только аннотация транзакции
      • метод_2 - Распространение.МАНДАТОРИЯ)
      • Вывод: метод_2 будет сохраняться в базе данных.
      • Результат: метод_1 будет сохраняться в базе данных. - Здесь основная Внешняя существующая транзакция, используемая как для метода 1, так и для 2
      • outerMethod - С транзакцией
      • method_1 - Распространение.МАНДАТОРИЯ) -
      • method_2 - Только аннотация транзакции и исключение исключений
      • Выход: в базе данных нет записи, что означает откат.
      • outerMethod - С транзакцией
      • method_1 - Propagation.REQUIRES_NEW)
      • method_2 - Propagation.REQUIRES_NEW) и выбрасывает исключение 1/0
      • Вывод: метод_2 выдает исключение, поэтому запись method_2 не сохраняется.
      • Результат: метод_1 будет сохраняться в базе данных.
      • Выход: Откат для метода_1

Уровень изоляции определяет, как изменения, внесенные в какой-либо репозиторий данных одной транзакцией, влияют на другие одновременные одновременные транзакции, а также то, как и когда эти измененные данные становятся доступными для других транзакций. Когда мы определяем транзакцию с использованием Spring Framework, мы также можем настроить, на каком уровне изоляции будет выполняться одна и та же транзакция.

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

}

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

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

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

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

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

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

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

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

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

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

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

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





propagation