Hibernate. Сессия и транзакции: как это работает? (Часть 3)

Во второй части мы говорили о том как происходит выборка из БД, сохранение и удаление в HIbernate. В этой части мы поговорим о том как работают транзакции.

7) Декларативное управление транзакциями

Для декларативного управления транзакциями мы будем использовать Spring Framework. Управление транзакциями осуществляется через менеджер транзакций. Вместо вызовов session.openTransaction() и session.commit() используется аннотация @Transactional. В конфигурации контекста приложения должно присутствовать следующее:

 <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
 <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
 </bean>

 <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
 <property name="sessionFactory" ref="sessionFactory"/>
 </bean>
 <tx:annotation-driven transaction-manager="transactionManager"/>

Здесь мы определили бин transactionManager, к которому привязан бин sessionFactory. Класс HibernateTransactionManager является реализацией общего интерфейса org.springframework.transaction.PlatformTransactionManager для SessionFactory библиотеки Hibernate.annotation-driven указывает менеджеру транзакций обрабатывать аннотацию @Transactional.


@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {ObjectNotFoundException.class, ConstraintViolationException.class})
public Long saveTask(Long userId) {
Session session = sessionFactory.getCurrentSession();
Tasks task = new Task();
task.setName("Задача 1");
task.setDefinition("Задача 1");
task.setTaskDate(new Date());
task.setUser((User) session.load(User.class, userId));
session.saveOrUpdate(task);
return task.getTaskId();
}

Аннотация @Transactional указывает, что метод должен выполняться в транзакции. Менеджер транзакций открывает новую транзакцию и создаёт для неё экземпляр Session, который доступен через sessionFactory.getCurrentSession(). Все методы, которые вызываются в методе с данной аннотацией, также имеют доступ к этой транзакции, потому что экземпляр Session является переменной потока (ThreadLocal). Вызов sessionFactory.openSession() откроет совсем другую сессию, которая не связана с транзакцией.

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

Параметр propagation самый интересный. Он указывает принцип распространения транзакции. Может принимать любое значение из перечисления org.springframework.transaction.annotation.Propagation. Приведём пример:


@Autowired
private SessionFactory sessionFactory;

@Autowired
private UserDao userDao;

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {ConstraintViolationException.class})
public Long saveTask(Long userId) {
Session session = sessionFactory.getCurrentSession();
User user = userDao.getUserByLogin("user1");
Tasks task = new Tasks();
task.setName("Задача 1");
...
task.setUser(user);
session.saveOrUpdate(task);
return task.getTaskId();
}

Метод UserDao.getUserByLogin() также может быть помечен аннотацией @Transactional. И здесь параметр propagation определит поведение метода UserDao.getUserByLogin() относительно транзакции метода saveTask():

  • Propagation.REQUIRED — выполняться в существующей транзакции, если она есть, иначе создавать новую.

  • Propagation.MANDATORY — выполняться в существующей транзакции, если она есть, иначе генерировать исключение.
  • Propagation.SUPPORTS — выполняться в существующей транзакции, если она есть, иначе выполняться вне транзакции.
  • Propagation.NOT_SUPPORTED — всегда выполняться вне транзакции. Если есть существующая, то она будет остановлена.
  • Propagation.REQUIRES_NEW — всегда выполняться в новой независимой транзакции. Если есть существующая, то она будет остановлена до окончания выполнения новой транзакции.

  • Propagation.NESTED — если есть текущая транзакция, выполняться в новой, так называемой, вложенной транзакции. Если вложенная транзакция будет отменена, то это не повлияет на внешнюю транзакцию; если будет отменена внешняя транзакция, то будет отменена и вложенная. Если текущей транзакции нет, то просто создаётся новая.
  • Propagation.NEVER — всегда выполнять вне транзакции, при наличии существующей генерировать исключение.

Рекомендую к прочтению материал о транзакциях. Следует помнить, что использование транзакций несёт дополнительные издержки в производительности.

(Visited 1 664 times, 3 visits today)

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.