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