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



    背景

    近期在公司做的一个项目,用的是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层事务没有回滚。改起来非常easy,直接把代码中的黄色块改成PreparedStatement ps = con.prepareStatement(sql);就能够了。


    总结

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

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

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

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

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



  • 相关阅读:
    HTML+JSP的登录界面数据库链接
    链式前向星模板
    【洛谷【模板】最小生成树】
    【洛谷P1090 合并果子】
    Dijkstra【迪杰斯特拉算法】
    Bellman-Ford&&SPFA
    【一本通1248:Dungeon Master&&洛谷UVA532 Dungeon Master】
    【一本通1329:【例8.2】细胞&&洛谷P1451 求细胞数量】
    Floyed-Warshall【弗洛伊德算法】
    广搜
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5385744.html
Copyright © 2020-2023  润新知