zoukankan      html  css  js  c++  java
  • Spring事务管理使用

    发现问题

    最近,碰到一个问题,再用spring实现事务管理的时候,发现不起作用,在出异常时,并不会回滚数据库操作。

    我想实现的功能如下:

    @Transactional(isolation=Isolation.DEFAULT,readOnly=false,propagation=Propagation.REQUIRED,rollbackFor=Exception.class)
    public boolean someService() {
        mapper.insert();
        mapper.insert();
    }

    TransactionManager的配置如下:

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    但是在测试中,在service中手动抛出异常后,第一条insert语句并不会回滚。

    之后研究了很久,在控制台打印的消息显示,并没有开启事务。那么我分析应该有下面几种可能:

    1. 配置错误
    2. JDBC的AutoCommit没有设置为false
    3. 控制台信息显示,在Service中,insertA在jdbt连接池中取出了一个连接,插入完后,便将连接返回给了连接池,所以在service中,有多个数据库连接分别处理不同的insert语句。这也可能导致事务失败。

    下面来逐步排除:

    AutoCommit=true,这个是有可能的,但是通过查看Spring的源码,发现Spring在事务中,是会检测当前连接的AutoCommit选项并把它设置为False,所以排除

    最后,通过查看Spring关于Transaction的文档,发现上面说了,如果使用的是DataSourceTransactionManager,那么只能在一个数据库连接上开启事务。那么这么说,应该是数据库连接池的问题了?或者是Spring自己并没有开启事务管理,而是将事务管理委托给了Mysql去实现?这个是有可能的,所以后面再看。

    解决问题

    然后,我选择使用了切面Aop来实现Spring的事务管理。步骤如下:

    //注入transactionManager
    @Autowired() @Qualifier("transactionManager1")
    private PlatformTransactionManager  transactionManager;

    这一步是获得自动注入的 transactionManager实例,之后使用TransactionTemplate

    //编程式开启事务
    TransactionTemplate template = new TransactionTemplate(transactionManager);

    是不是很像JDBCTemplate?那么一样的使用内部类实现回调:

            //在这边也可以取消返回值,设为void
            boolean result = (Boolean) template.execute(new TransactionCallback<Object>() {
            public Object doInTransaction(TransactionStatus transactionStatus) {
                try {
                    mapper.insertA();
                    throw new SQLException();
                    mapper.insertB();
                } catch (Exception e) {mapper.insertB();
                    //出异常时,手动设置回滚
                    transactionStatus.setRollbackOnly();  
                    e.printStackTrace();
                    return false;
                }
                return true;
              }
            });

    这样,就可以使用Aop手动开启事务了。

    经过测试,在两个插入之间手动抛出了异常,insertA的插入被回滚了。从控制台输出信息来看,insertA插入成功后,即把jdbc返回到连接池了,并且事务管理配置的依然是DataSourceTransactionManager,说明前面的数据库连接池的问题被排除了。

    分析原因

    那么就剩下配置错误了。

    实验多次以后,我发现如下配置是可以让声明式事务管理起作用的:

    Spring的配置中,取消注入Controller

    <context:component-scan base-package="com.test">  
          <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />   
    </context:component-scan> 

    SpringMVC的配置,取消注入Service

    <context:component-scan base-package="com.test">  
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />  
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />  
    </context:component-scan>  

    配置事务的Service实现某个接口

    @Service
    public class Test() {
        @Transactional(isolation=Isolation.DEFAULT,readOnly=false,propagation=Propagation.REQUIRED,rollbackFor=Exception.class)
        public boolean someService() {
            mapper.insert();
            mapper.insert();
        }
    }

    这样便可以开启事务,并在遇到异常时回滚了。那为什么原来的配置就不行呢?

    根本原因是因为spring的context是父子容器,由ServletContextListener 加载spring配置文件产生的是父容器,springMVC加载配置文件产生的是子容器,子容器对Controller进行扫描装配时装配了@Service注解的实例 (@Controller 实例依赖@Service实例),而该实例理应由父容器进行初始化以保证事务的增强处理,所以此时得到的将是原样的Service,没有经过事务加强处理,故而没有事务处理能力

    所以在spring的配置中我们要手动exclude controller的注入,不扫描带有@Controller注解的类。因为这些类已经随容器启动时,在servlet-context中扫描过一遍了

    在springMVC的配置中扫描业务组件,让springMVC不扫描带有@Service注解的类(留在spring中扫描@Service注解的类),防止事务失效。

    在需要配置事务管理的Service类中,需要让类继承一个或几个接口,是因为如果不继承接口,那么会使用cglib动态代理去实现,那么事务管理也会失效。如果实现了接口,那么便会使用jdk自带的动态代理去实现,这样便可以成功的开启事务了。

  • 相关阅读:
    [LUOGU] P3275 [SCOI2011]糖果
    [BZOJ] 2287: 【POJ Challenge】消失之物
    [BZOJ] 2131: 免费的馅饼
    [JZOJ] 5835. Prime
    [JZOJ] 5837.Omeed
    UF_CAMGEOM_ask_custom_points 封装缺陷
    NX Open 切削层加载
    NX Open 图层说
    c++ Dll调用
    VC操作Excel文件编程相关内容总结
  • 原文地址:https://www.cnblogs.com/edwinchen/p/4137707.html
Copyright © 2011-2022 走看看