Spring @Transactional does not rollback checked exceptions

时光毁灭记忆、已成空白 提交于 2021-01-28 05:13:00

问题


I have been stuck for a while to make Spring rollback a transaction when a checked exception is thrown using @Transactional(rollbackFor). Here is my code:

The data access class:

@Repository
public class CustomerDao {

    @Autowired
    private SessionFactory sessionFactory;
    
    public void willRollback() throws CheckedException {
        sessionFactory.getCurrentSession().persist(new SpringCustomer(null, "roll back"));
        throw new CheckedException();
    }
}

Where CheckedException is just a simple checked exception:

public class CheckedException extends Exception {}

The service class CustomerService:

@Service
public class CustomerService {
    
    @Autowired
    private CustomerDao customerDao;
    
    @Transactional(transactionManager = "hibernateTransactionManager", rollbackFor = CheckedException.class)
    public void willRollback() throws CheckedException {
        customerDao.willRollback();
    }
}

Beans configurations:

@Configuration
public class BasicConfig {
    
    @Bean
    public DataSource dataSource() {

        DriverManagerDataSource ds = new DriverManagerDataSource();

        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/test_hibernate?useSSL=false");
        ds.setUsername("root");
        ds.setPassword("Passw0rd");

        return ds;
    }

    @Bean
    public LocalSessionFactoryBean sessionFactory() {

        LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
        localSessionFactoryBean.setDataSource(dataSource());
        localSessionFactoryBean.setPackagesToScan("com.home.exception.checked");

        Properties hibernateProperties = new Properties();
        hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
        hibernateProperties.put("hibernate.show_sql", "true");
        hibernateProperties.put("hibernate.hbm2ddl.auto", "update");
        localSessionFactoryBean.setHibernateProperties(hibernateProperties);

        return localSessionFactoryBean;
    }

    @Bean
    public HibernateTransactionManager hibernateTransactionManager() {
        return new HibernateTransactionManager(sessionFactory().getObject());
    }

}

And finally, here is my main class:

@Configuration
@ComponentScan
@EnableTransactionManagement
public class Main {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx = 
                new AnnotationConfigApplicationContext(Main.class); 
        
        try {
            ctx.getBean(CustomerService.class).willRollback();
        } catch (CheckedException e) {
            e.printStackTrace();
        }
        
        ctx.close();
    }
}

I've read many answers related to these questions, all of them suggesting to call the transactional method from outside the proxy itself, which I did. Regardless, the entity is always persisted in the database anyway and the transaction is not rolled back.

Any help would be appreciated.


UPDATE: As per @kavithakaran-kanapathippillai answer, I debugged the TransactionAspectSupport.completeTransactionAfterThrowing() method, and the following methods as well, and found that the rollback logic is executed. The entity still appears when querieng the db though. So, I enabled db logging to see what queries are run, and I found the following:

2020-06-28T07:29:48.516038Z   391 Query SET autocommit=0
2020-06-28T07:29:48.520253Z   391 Query insert into spring_customer (name) values ('roll back')
2020-06-28T07:29:48.524969Z   391 Query rollback
2020-06-28T07:29:48.526026Z   391 Query SET autocommit=1

I don't know why this happens but it looks like the Spring rollback is working fine.


UPDATE2: The problem was due to my table was using the MyISAM engine (non-transactional engine). Once I changed it to InnoDB (a transactional engine), the record is not persisted anymore.


回答1:


The following method is where spring checks whether to rollback when an exception is thrown. Class name is TransactionAspectSupport. You can put a break point and see whether txInfo.transactionAttribute.rollbackOn(ex) is evaluating to true

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo,
                                                Throwable ex) {
  if (txInfo != null && txInfo.getTransactionStatus() != null) {
     .....
    if (txInfo.transactionAttribute != null && 
        txInfo.transactionAttribute.rollbackOn(ex)) {

Reference:

TransactionAspectSupport.java



来源:https://stackoverflow.com/questions/62613814/spring-transactional-does-not-rollback-checked-exceptions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!