Аннотация @Transactional. Как откатиться?

91

Я успешно использовал эту аннотацию для класса Дао. И откат для тестов работает.

Но теперь мне нужно откатывать реальный код, а не только тесты. Есть специальные аннотации для использования в тестах. Но какие аннотации предназначены для нетестового кода? Для меня это большой вопрос. Я потратил на это уже день. Официальная документация мне не подошла.

class MyClass { // this does not make rollback! And record appears in DB.
        EmployeeDaoInterface employeeDao;

        public MyClass() {
            ApplicationContext context = new ClassPathXmlApplicationContext(
                    new String[] { "HibernateDaoBeans.xml" });
            employeeDao = (IEmployeeDao) context.getBean("employeeDao");
         }

        @Transactional(rollbackFor={Exception.class})
    public void doInsert( Employee newEmp ) throws Exception {
        employeeDao.insertEmployee(newEmp);
        throw new RuntimeException();
    }
}

сотрудникДао

@Transactional
public class EmployeeDao implements IEmployeeDao {
    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void insertEmployee(Employee emp) {
        sessionFactory.getCurrentSession().save(emp);
    }
}

А вот тест, для которого аннотации работают хорошо:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/HibernateDaoBeans.xml" })
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)
@Transactional
public class EmployeeDaoTest {

    @Autowired
    EmployeeDaoInterface empDao;

    @Test
    public void insert_record() {
       ...
       assertTrue(empDao.insertEmployee(newEmp));
    }

HibernateDaoBeans.xml

   ...
<bean id="employeeDao" class="Hibernate.EmployeeDao">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
    <tx:annotation-driven transaction-manager="txManager"/>

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
   ...



** ДА, я откатил транзакцию. Я только что добавил БИН для сервиса ... и тут начинает работать аннотация @Transactional :-) **

<bean id="service" class="main.MyClass">
    <property name="employeeDao" ref="employeeDao" />
</bean>

Всем спасибо, Россия вас не забудет!

Московский мальчик
источник

Ответы:

163

Просто выбросьте любой RuntimeExceptionиз метода, отмеченного как @Transactional.

По умолчанию все RuntimeExceptionтранзакции отката, а проверенные исключения - нет. Это наследие EJB. Вы можете настроить это, используя параметры аннотации rollbackFor()и noRollbackFor():

@Transactional(rollbackFor=Exception.class)

Это откатит транзакцию после выброса любого исключения.

Томаш Нуркевич
источник
1
Я пробовал. Это не работает! Я рассказываю об операции отката не прямо в объекте Dao, возможно, это сработает. Рассказываю о другом объекте, использующем последовательность вызова методов Dao, использующих @Transactional. И я пытаюсь добавить ту же аннотацию для своего класса, которая называет это Дао. Но здесь это не работает.
Moscow Boy
5
Это действительно так работает :-). Если у вас есть служба, вызывающая несколько DAO, она также должна иметь @Transactionalаннотацию. В противном случае каждый вызов DAO запускается и фиксирует новую транзакцию до того, как вы создадите исключение в службе. Суть в следующем: исключение должно покинуть метод, помеченный как @Transactional.
Tomasz Nurkiewicz
Посмотрите на добавленный код в первом посте. Часть кода у меня есть. И после выполнения у меня есть запись в БД. => откат пока не работает.
Moscow Boy
Исключение из DAO не откатывает транзакцию? Вы org.springframework.orm.hibernate3.HibernateTransactionManagerнастроили в своем контексте Spring? Если включить org.springframework.transactionлоггер, он ничего интересного не показывает?
Tomasz Nurkiewicz
1
Если вы используете Spring Boot и позволяете ему автоматически настраивать DataSource, он будет использовать HikariDataSource, для которого по умолчанию установлено значение true для автоматической фиксации. Вам необходимо установить значение false: hikariDataSource.setAutoCommit (false); Я внес это изменение в класс @Configuration при настройке bean-компонента DataSourceTransactionManager.
Алекс
83

или программно

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Стефан К.
источник
12
Да. Потому что иногда нужно откатиться без выкидывания ошибки.
Erica Kane
2
Вы палочка-выручалочка.
Walls
Это более подходящий способ вызвать откат imo, в идеале вы не хотите создавать исключения для известных или контролируемых условий.
jalpino
2
Иногда не получается. Например, в моем случае у меня есть транзакционная служба и нетранзакционный репозиторий. Я вызываю репозиторий из службы, поэтому он должен участвовать в транзакции, открытой службой. Но если я вызываю ваш код из своего нетранзакционного репозитория, он генерирует исключение NoTransactionException, несмотря на то, что транзакция существует.
Виктор Домбровский
3
какую транзакцию вы хотите откатить в своем «не транзакционном репозитории»?
Стефан К.
5

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

Я предполагаю, что вы здесь используете Spring. И я предполагаю, что аннотации, на которые вы ссылаетесь в своих тестах, являются аннотациями на основе весенних тестов.

Рекомендуемый способ указать инфраструктуре транзакций Spring Framework, что работа транзакции должна быть отменена, - это выбросить исключение из кода, который в настоящее время выполняется в контексте транзакции.

и обратите внимание, что:

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

Алекс Барнс
источник
1

Для меня rollbackFor было недостаточно, поэтому мне пришлось поставить это, и он работает, как ожидалось:

@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)

Я надеюсь, что это помогает :-)

Роберто Родригес
источник
1
нет, это совсем не помогает, TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();ниже вы помогли
Enerccio