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层事务没有回滚,改起来很简单,直接把代码中的黄色块改成PreparedStatement
ps = con.prepareStatement(sql);就可以了。

总结

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

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

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

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

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-07 16:15:28

Spring @Transaction配置示例及发生不回滚原因深度剖析的相关文章

Spring常用配置示例

Spring 是一款Java平台的开源框架,是为解决企业级应用程序开发的复杂性而创建的,通过良好的分层架构让开发人员能够专注于业务逻辑的开发. Spring框架是一个分层架构,由不同的模块组成,构成spring的每个组件或模块都可以单独使用或者多个模块配合使用,以实现不同的功能需求.Spring框架的模块结构如下图所示: SpringCore是Spring框架的核心模块,提供spring框架的基本功能,使用工厂模式BeanFactory通过控制反转(IoC).依赖注入(DI)等实现对beans的

Spring学习笔记——Spring事务只对运行时异常回滚

我们在使用Spring时候一般都知道事务在遇到异常的时候会回滚,岂不知Spring的事务默认只有在发生运行时异常即:RunTimeException时才会发生事务,如果一个方法抛出Exception或者Checked异常Spring的事务并不会回滚. 下面我们来看看异常的分类,异常一般分为Checked异常和RunTime异常. CheckedException: Java认为Checked异常都是可以被处理的异常,所以Java程序必须显式的处理Checked异常,如果程序没有处理checked

Spring学习笔记——Spring事务仅仅对执行时异常回滚

我们在使用Spring时候一般都知道事务在遇到异常的时候会回滚.岂不知Spring的事务默认仅仅有在发生执行时异常即:RunTimeException时才会发生事务,假设一个方法抛出Exception或者Checked异常Spring的事务并不会回滚. 以下我们来看看异常的分类.异常一般分为Checked异常和RunTime异常. CheckedException: Java觉得Checked异常都是能够被处理的异常,所以Java程序必须显式的处理Checked异常,假设程序没有处理checke

从@Async案例找到Spring框架的bug:exposeProxy=true不生效原因大剖析+最佳解决方案【享学Spring】

https://cloud.tencent.com/developer/article/1497700 前言 本文标题包含有'靓丽'的字眼:Spring框架bug.相信有的小伙伴心里小九九就会说了:又是一篇标题党文章. 鉴于此,此处可以很负责任的对大伙说:本人所有文章绝不哗众取宠,除了干货只剩干货. 相信关注过我的小伙伴都是知道的,我只递送干货,绝不标题党来浪费大家的时间和精力~那无异于谋财害命(说得严重了,不喜勿喷) 关于标题党的好与坏.优与劣,此处我不置可否 本篇文章能让你知道exposeP

导致spring事务配置不起作用的一种原因

@Component public class AnalyticsApplication { @Autowired private InitializationActionService initializationActionService; @PostConstruct @Transactional("transactionManager") // in here, the transaction configuration does not work public void sy

关于Spring 声明式事务处理时,throws exception不回滚的问题

摘自 http://cn-done.iteye.com/blog/775519 前一段时间,项目代码评审,发现有TX不使用Spring的事务处理,而直接封装方法,手动进行数据的回滚,得悉原因是:抛出异常以后事务不起作用,没有回滚.这理由顿时让人很无语,不过还算个聪明的TX,知晓另辟蹊径,但是在insert的时候,手动回滚直接delete就可以了,如果是update,不晓得还会有什么更犀利的办法. 仔细评审代码细节,发现该TX压根没有按照框架原先设计在service层throws Business

spring + myBatis 常见错误:注解事务不回滚

最近项目在用springMVC+spring+myBatis框架,在配置事务的时候发现一个事务不能回滚的问题. 刚开始配置如下:springMVC.xml配置内容: spring.xml配置内容 从上面两个配置文件看出,开始的时候我把Service配置在springMVC中的.但是,事务注解我配置在了spring中.这样就会出现问题了.因为spring的容器(applicationContext)和springMVC的(applicationContext)是不同的. spring容器加载得时候

Spring事务配置方式(一) 注解方式配置

注解方式配置事务 引用自 http://www.cnblogs.com/younggun/archive/2013/07/16/3193800.html <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="d

Spring @Transactional注解不回滚不起作用无效

这几天在项目里面发现我使用@Transactional之后,抛了异常居然不回滚.后来终于找到了原因. 如果你也出现了这种情况,可以从下面开始排查. 一.特性 先来了解一下@Transactional注解的特性吧,可以更好排查问题 1. service类标签(一般不建议在接口上)上添加@Transactional,可以将整个类纳入spring事务管理,在每个业务方法执行时都会开启一个事务,不过这些事务采用相同的管理方式. 2. @Transactional 注解只能应用到 public 可见度的方