Всем добрый день!
Что ж, конец месяца у нас всегда интенсивные, вот и тут остался всего день до старта второго потока курса «Разработчик на Spring Framework» — замечательного и интересного курса, который ведёт не менее прекрасный и злой Юрий (как его называют некоторые студент за уровень требований в ДЗ), так что давайте рассмотрим ещё один материал, который мы подготовили для вас.
Поехали.
Введение
Большую часть времени разработчики не придают значения управлению транзакциями. В результате либо большую часть кода приходится переписывать позже, либо разработчик реализует управление транзакциями без знаний того, как оно на самом деле должно работать или какие аспекты необходимо использовать конкретно в их случае.
Важный аспект в управлении транзакциями — определение правильных границы транзакции, когда транзакция должна начинаться и когда заканчиваться, когда данные должны быть добавлены в БД и когда они должны быть откачены обратно (в случае возникновения исключения).
Самый важный аспект для разработчиков — это понять как реализовать управление транзакциями в приложении наилучшим образом. Поэтому давайте рассмотрим различные варианты.
Способы управления транзакциями
Транзакции могут управляться следующими способами:
1. Программное управление путем написания пользовательского кода
Это старый способ управления транзакциями.
EntityManagerFactory factory = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_NAME"); EntityManager entityManager = entityManagerFactory.createEntityManager();
Transaction transaction = entityManager.getTransaction()
try
{
transaction.begin();
someBusinessCode();
transaction.commit();
}
catch(Exception ex)
{
transaction.rollback();
throw ex;
}
TransactionTemplate
(рекомендовано командой Spring):<!-- Initialization for data source -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- Initialization for TransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Definition for ServiceImpl bean -->
<bean id="serviceImpl" class="com.service.ServiceImpl">
<constructor-arg ref="transactionManager"/>
</bean>
Service
:public class ServiceImpl implements Service
{
private final TransactionTemplate transactionTemplate;
// используйте инъекцию в конструктор чтобы предоставить PlatformTransactionManager
public ServiceImpl(PlatformTransactionManager transactionManager)
{
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
// параметры транзакции здесь могут быть установлены явно, если это необходимо, обеспечивая больше контроля
// Также мы можем сделать это через xml файл
this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); this.transactionTemplate.setTimeout(30); //30 секунд
/// и так далее
public Object someServiceMethod()
{
return transactionTemplate.execute(new TransactionCallback()
{
// код в этом методе выполняется в транзакционном контексте
public Object doInTransaction(TransactionStatus status)
{
updateOperation1();
return resultOfUpdateOperation2();
}
});
}}
TransactionCallbackWithoutResult
с анонимным классом, как показано ниже:transactionTemplate.execute(new TransactionCallbackWithoutResult()
{
protected void doInTransactionWithoutResult(TransactionStatus status)
{
updateOperation1();
updateOperation2();
}
});
TransactionTemplate
потокобезопасные, поэтому поддерживают не все диалоговые состояния.TransactionTemplate
тем не менее поддерживают конфигурационное состояние, поэтому, если классу необходимо использовать TransactionTemplate с разными настройками (например, другой уровень изоляции), то вам нужно создать два различных экземпляра TransactionTemplate, хотя в некоторых классах может использоваться один экземпляр TransactionTemplate.PlatformTransactionManager
напрямую:<!-- Initialization for data source -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- Initialization for TransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
public class ServiceImpl implements Service
{
private PlatformTransactionManager transactionManager;
public void setTransactionManager( PlatformTransactionManager transactionManager)
{
this.transactionManager = transactionManager;
}
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// Явное указание имени транзакции - это то, что может быть сделано только программно
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try
{
// выполняем вашу бизнес-логику здесь
}
catch (Exception ex)
{
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
}
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/>
<tx:annotation-driven transaction-manager="txManager"/>
@EnableTransactionManagement
в ваш конфигурационный файл, как показано ниже:@Configuration
@EnableTransactionManagement
public class AppConfig
{
...
}
@Transactional
в сравнении с аннотирующими интерфейсами.proxy-target-class = «true»
) или сплетающий аспект (mode = «aspectj»
), тогда параметры транзакции не распознаются инфраструктурой проксирования и сплетения, например Транзакционное поведение не будет применяться.@Transactional
в класс (метод класса) или интерфейс (метод интерфейса).<tx:annotation-driven proxy-target-class="true">
proxy-target-class="false"
@Transactional
может быть помещена перед определением интерфейса, метода интерфейса, определением класса или публичным методом класса.@Transactional
) имели разные настройки атрибутов, такие как уровень изоляции или уровень распространения, разместите аннотацию на уровне метода, чтобы переопределить настройки атрибутов уровня класса.@Transactional
.@Transactional
@Transactional (isolation=Isolation.READ_COMMITTED)
Isolation.DEFAULT
tx
), что для текущего tx
должен использоваться следующий уровень изоляции. Должен быть установлен в точке, откуда начинается tx
, потому что мы не можем изменить уровень изоляции после запуска tx.@Transactional(timeout=60)
@Transactional(propagation=Propagation.REQUIRED)
REQUIRED
.REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVER
и NESTED
.@Transactional (rollbackFor=Exception.class)
rollbackFor=RunTimeException.class
Checked Exception
.@Transactional (noRollbackFor=IllegalStateException.class)
@Transactiona
l. В большинстве случаев возникает путаница, где должна размещаться аннотация: на сервисном уровне или на уровне DAO?@Transactional
: Сервисный или DAO уровень?@Transactional
, сервисный уровень должен содержать поведение варианта использования на уровне детализации для взаимодействия пользователя, которое логически переходит в транзакцию.@Transactional
в уровень DAO и если ваш уровень DAO будет повторно использоваться разными службами, тогда будет сложно разместить его на уровне DAO, так как разные службы могут иметь разные требования.К сожалению, не доступен сервер mySQL