Spring @Transaction配置示例及发生不回滚原因深度剖析

本文探讨了在Spring MVC项目中遇到的事务处理问题。通过分析配置、异常处理和DAO层代码,发现由于连接池获取的连接未被事务管理导致回滚失败。解决方案是确保所有操作在同一个受事务管理的连接上进行,确保`autoCommit`属性为false。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


背景

最近在公司做的一个项目,用的是SpringMVC框架,数据库用的是MySql,刚开始并没有加入事务,后因业务需要必须事务处理。


问题的产生和解决

使用事务,直接问百度,我选择的是注解的方式。


在配置文件中配置事务管理器和驱动:

<tx:annotation-driven transaction-manager="transactionManager"/>
      
       <bean
              id="transactionManager"
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource">
                     <ref bean="dataSource"/>
              </property>
       </bean>


然后直接在service层加注解


package com.my.service.impl;

import java.sql.SQLException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.my.constants.ServiceException;
import com.my.dao.TestDao;
import com.my.service.TestService;

@Service
public class TestServiceImpl implements TestService{

    @Autowired
    private TestDao testDao;
    
    @Override
    @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { ServiceException.class })
    public int insertUser(String userName) throws ServiceException {
        int id = 0;
        try {
            id = testDao.insertUser(userName);
        } catch (SQLException e) {
            throw new ServiceException();
        }
        return id;
    } 
}


自然地,rollback的异常要和service抛出的异常一样才会回滚。

然后自认为代码肯定没有问题,可是多次debug后到数据库取看都没有回滚,于是就直接在代码中加入错误代码int i = 5/0,并把exception的捕获修改一下


package com.my.service.impl;

import java.sql.SQLException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.my.constants.ServiceException;
import com.my.dao.TestDao;
import com.my.service.TestService;

@Service
public class TestServiceImpl implements TestService{

    @Autowired
    private TestDao testDao;
    
    @Override
    @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { ServiceException.class })
    public int insertUser(String userName) throws ServiceException {
        int id = 0;
        try {
            id = testDao.insertUser(userName);
			int i = 5/0;
        } catch (Exception e) {
            throw new ServiceException();
        }
        return id;
    }
    
}


用上面的代码多次调试,始终没有回滚。

然后自然想到,可能Dao层有问题,然后去看Dao层代码,似乎真的有问题:


package com.my.dao.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

import com.my.dao.TestDao;

@Repository
public class TestDaoImpl implements TestDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public int insertUser(final String userName) throws SQLException {
        final String sql = "INSERT INTO USER(NAME) VALUES(?);";
        KeyHolder keyHolder = new GeneratedKeyHolder();

        jdbcTemplate.update(new PreparedStatementCreator() {
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                PreparedStatement ps = jdbcTem-plate.getDataSource().getConnection().prepareStatement(sql);
                ps.setString(1, userName);

                return ps;
            }
        }, keyHolder);

        return keyHolder.getKey().intValue();
    }
}


错误可能就在代码黄色块。于是debug进去,看到Connectioncon中的autoCommit属性是false的,显然是被service层的事务管理到的,而jdbcTemplate.getDataSource().getConnection()是到链接池重新获取的连接,这个连接显然没有被事务管理,它的autoCommit属性显然是true,所以这使得service层事务没有回滚,改起来很简单,直接把代码中的黄色块改成PreparedStatement ps = con.prepareStatement(sql);就可以了。


总结

 

遇到Springmvc事务不能回滚,解决的步骤:

1.  检查配置文件里面有没有加入事务管理配置和驱动;

2.  检查数据库是否支持事务(例如MySql4.0 支持事务,Engine:InnoDB);

3.  检查代码块是否抛出异常,且事务的rollback的异常是抛出异常或者是抛出异常的父类;

4.  检查事务覆盖的代码块中的所有Connection是否都被这个事务覆盖(debug检查所有connection的autoCommit属性是不是被事务改成了false)。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值