Spring @Transactional 不会回滚已检查的异常 [英] Spring @Transactional does not rollback checked exceptions

查看:38
本文介绍了Spring @Transactional 不会回滚已检查的异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当使用 @Transactional(rollbackFor) 抛出已检查的异常时,我一直坚持让 Spring 回滚事务.这是我的代码:

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:

数据访问类:

@Repository
public class CustomerDao {

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

其中 CheckedException 只是一个简单的检查异常:

Where CheckedException is just a simple checked exception:

public class CheckedException extends Exception {}

服务类CustomerService:

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

Beans 配置:

@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.

任何帮助将不胜感激.

更新: 根据@kavithakaran-kanapathippillai 的回答,我调试了 TransactionAspectSupport.completeTransactionAfterThrowing() 方法,以及以下方法,发现回滚逻辑被执行.尽管如此,当查询数据库时实体仍然出现.因此,我启用了数据库日志记录以查看运行了哪些查询,我发现了以下内容:

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

我不知道为什么会发生这种情况,但看起来 Spring 回滚工作正常.

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

UPDATE2: 问题是由于我的表使用的是 MyISAM 引擎(非事务引擎).一旦我将其更改为 InnoDB(事务引擎),该记录就不再持久化了.

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.

推荐答案

下面这个方法就是spring在抛出异常时检查是否回滚的地方.类名是 TransactionAspectSupport.您可以设置一个断点并查看 txInfo.transactionAttribute.rollbackOn(ex) 是否评估为 true

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)) {

参考:

TransactionAspectSupport.java

这篇关于Spring @Transactional 不会回滚已检查的异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆