Главная
Java
Spring
@Transactional
import org.springframework.transaction.annotation.Transactional;Транзакциями могут называтся действия которые полностью соответствуют ACID !!!
Если один из пунктов ACID не выполняется , то оно не может называться Транзакцией !! (например в MongoDB не выполняется Durability )
Транзакция это набор операций, которые могут быть либо целиком и успешно выполнены ,либо полностью не выполнены.
транзакции необходимы для поддержки целостности и согласованности данных
<tx:annotation-driven transaction-manager="transactionManager"/>
Устанавливайте аннотацию @Transactional в слое сервиса (Service layer), а не в слое DAO !!!
По умолчанию @Transactional без параметров , является REQUIRED, значением атрибута "readOnly" – false, уровень изоляции соответствует уровню изоляции по умолчанию для базы данных (как правило, это READ_COMMITTED), и транзакция НЕ БУДЕТ откатываться в случае контролируемых исключений (checked exception) !!!
Странной особенностью флага "@Transactional(readOnly = true)" является то, что он вступает в силу только с началом новой транзакции ! В ORM окружении необходимо использовать этот флаг вместе с Propagation.SUPPORTS.
Не используйте аннотацию @Transactional на приватных, protected или default методах. Добавление аннотации к методами с private, protected или default модификаторами доступа не выбросит исключение.Однако аннотация будет проигнорирована !!!
лучше вообще не использовать аннотацию @Transactional при чтении данных из базы
Транзакциями в Spring управляют с помощью Declarative Transaction Management (программное управление). Используется аннотация @Transactional для описания необходимости управления транзакцией. В файле конфигурации нужно добавить настройку transactionManager для DataSource.
Распространение транзакций :
- Propagation.REQUIRED — применяется по умолчанию. При входе в @Transactional метод будет использована уже существующая транзакция или создана новая транзакция, если никакой ещё нет
- Propagation.REQUIRES_NEW — второе по распространённости правило. Транзакция всегда создаётся при входе метод с Propagation.REQUIRES_NEW, ранее созданные транзакции приостанавливаются до момента возврата из метода.
- Propagation.NESTED — корректно работает только с базами данных, которые умеют savepoints. При входе в метод в уже существующей транзакции создаётся savepoint, который по результатам выполнения метода будет либо сохранён, либо откачен. Все изменения, внесённые методом, подтвердятся только поздее, с подтверждением всей транзакции. Если текущей транзакции не существует, будет создана новая.
- Propagation.MANDATORY — обратный по отношению к Propagation.REQUIRES_NEW: всегда используется существующая транзакция и кидается исключение, если текущей транзакции нет.
- Propagation.SUPPORTS — метод с этим правилом будет использовать текущую транзакцию, если она есть, либо будет исполнятся без транзакции, если её нет.
- Propagation.NOT_SUPPORTED — одно из самых забавных правил. При входе в метод текущая транзакция, если она есть, будет приостановлена и метод будет выполняться без транзакции.
- Propagation.NEVER — правило, которое явно запрещает исполнение в контексте транзакции. Если при входе в метод будет существовать транзакция, будет выброшено исключение.
откат транзакций:
- rollbackFor = Exception.class - Значением этого параметра может быть либо класс исключения, либо массив подобных классов. По умолчанию откат происходит при rollbackFor=RunTimeException.class
- rollbackForClassName = "Name" - для задания имен исключений в строковом виде
- noRollbackFor = IllegalStateException.class - Означает что откат не должен происходить если целевой метод выбросил это исключение.
уровни изоляции транзакций:
- isolation = Isolation.READ UNCOMMITTED (dirty read («грязное чтение»)) - После INSERT данные сразу-же станут доступны для чтения. Тоесть еще до вызова COMMIT
- isolation = Isolation.READ COMMTITED(По умолчанию в PostgreSQL) - В данном случае прочитать данные возможно только после вызова COMMIT
- isolation = Isolation.REPEATABLE READ (по умолчанию в MySQL) - изменения данных, которые были прочитаны в транзакции ранее в транзакцию не попадают, другие транзакции не могут изменять данные, прочитанные этой транзакцией. Возможен эффект фантомного чтения, степерь параллельности транзакций выше, чем у Serializable.
- isolation = Isolation.SERIALIZABLE - 100% уровень изоляции , но приэтом сильно проседает скорость работы БД !!! все транзакции выполняются почти друг за другом. транзакции полностью изолируются друг от друга и ни одна транзакция ни коим образом не влияет на другие. На самом деле смысла использовать этот уровень нет
Для уровня изоляции Read committed допустимы следующие особые условия чтения данных::
- Неповторяемое чтение — транзакция повторно читает те же данные, что и раньше, и обнаруживает, что они были изменены другой транзакцией (которая завершилась после первого чтения).
- Фантомное чтение — транзакция повторно выполняет запрос, возвращающий набор строк для некоторого условия, и обнаруживает, что набор строк, удовлетворяющих условию, изменился из-за транзакции, завершившейся за это время.
Через аннотации :
аннотировать ваши классы @Transactional аннотацией, добавить @EnableTransactionManagement к @Configuration классу
Если работать через JPA , то надо создавать TransactionManager + JpaTransactionManager бины
@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{
@Bean
public LocalContainerEntityManagerFactoryBean
entityManagerFactoryBean(){
//...
}
@Bean
public PlatformTransactionManager transactionManager(){
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
entityManagerFactoryBean().getObject() );
return transactionManager;
}
}
-------------------------------------------------------------------------------------
@Service
@Transactional
public class FooService {
//...
}
Через XML :
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true"
transaction-manager="transactionManager" />
<!-- Creating TransactionManager Bean, since JDBC we are creating of type
DataSourceTransactionManager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
Или через JpaTransactionManager:
<!-- Configure the transaction manager bean -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
транзакции соответствуют свойствам ACID :
- Атомарность - транзакция может быть либо целиком выполнена , либо целиком удалена.
- Согласованность - состояние данных должно быть логически согласованным после выполнения транзакции
- Изолированность - в процессе работы транзакции другие выполняющиеся в это время транзакции не влияют на неё
- Надежность - что-бы не произошло, транзакция останется атомарной.
Этот метод помечен как @Transactional, что означает откат всех записей к предыдущему значению, если любая из операций в этом методе завершится неудачей, а также повторно бросит оригинальное исключение. Это значит, что если добавление одного из людей завершится ошибкой, то ни один из людей в итоге не добавится в таблицу BOOKINGS.
Полезные ссылки:
- Изоляция и распространение транзакций в Spring EasyJava
- Распространенные ошибки IBM
- http://spring-projects.ru
- Transaction Management Spring Docks
- Transactions with Spring and JPA Baeldung
- Как на самом деле работает @Transactional Spring? Блог Анатолия Корсакова