zoukankan      html  css  js  c++  java
  • spring入门(三)【事务控制】

    在开发中需要操作数据库,进行增、删、改操作的过程中属于一次操作,如果在一个业务中需要更新多张表,那么任意一张表的更新失败,整个业务的更新就是失败,这时那些更新成功的表必须回滚,否则业务会出错,这时就要用到事务,即这个业务的操作属于一个事务,事务具有原子性、隔离性、一致性、持续性。这时便用到了事务,事务控制的目的是保证一组操作要么全部成功,要么全部失败。spring提供了对事务的支持,在spring中主要有两种方式使用事务,一、编程式事务控制;二、声明式事务控制。

    一、编程式事务控制

    所谓编程式事务控制即通过编写代码的方式实现事务的控制。

    spring为了方便处理事务,提供了事务管理器,对事务的控制归根到底是通过事务管理器进行控制,在spring中所有的事务控制必须要有事务管理器。下面是一个编程式事务控制的例子,实现账户之间的转账,我们把对事务的控制放在系统的service层(分为controller层、service层、DAO层)来处理,下面是我的spring配置文件,

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:aop="http://www.springframework.org/schema/aop"
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
    	
    	>
    	<!--spring 自动检测-->
    	
    	<context:component-scan base-package="com.cn.study.day5" />
    	<!---->
    	<context:property-placeholder location="classpath:db.properties"/>
    	<!---->
    
         <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<property name="driverClassName">
    			<value>${db.driver}</value>
    		</property>
    		<property name="url">
    			<value>${db.url}</value>
    		</property>
    		<property name="username">
    			<value>${db.username}</value>
    		</property>
    		<property name="password">
    			<value>123456</value>
    		</property>
    	</bean>	
    	
    		<bean id="dao" class="com.cn.study.day5.service.inter.impl.AccountDaoImpl">
    		
    		<property name="dataSource" ref="dataSource"></property>
    	</bean>
    	
    	<!--配置事务管理器-->
    	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="dataSource"></property>
    		
    	</bean>
    	<!--事务管理器模板 方便使用事务-->
    	<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    		<property name="transactionManager" ref="transactionManager"></property>
    	</bean>
    
    </beans>
    

      配置了事务管理器,这里使用DataSourceTransactionManager,事务管理器有一个dataSource属性必须配置,这里使用ref属性引用上边的。有了事务管理器之后要使用事务还是比较麻烦,spring又提供了事务管理器模板,我们配置事务管理器模板,事务管理器模板需要一个事务管理器属性,我们引用上边的事务管理器。至此关于编程式的事务控制的配置文件已经准备完毕,下面进行编程式开发。由于,我们把事务控制放在service层,下面是我的service层的代码,

    package com.cn.study.day5.service.inter.impl;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.support.TransactionCallback;
    import org.springframework.transaction.support.TransactionCallbackWithoutResult;
    import org.springframework.transaction.support.TransactionTemplate;
    
    import com.cn.study.day5.service.inter.AccountDaoInter;
    import com.cn.study.day5.service.inter.AccountServiceIter;
    
    @Component
    public class AccountServiceImpl implements AccountServiceIter {
        @Autowired
        private AccountDaoInter adi;
        @Autowired
        private TransactionTemplate tt;
        //转账方法,由out向in转money元
        @Override
        public void transfer(final String out, final String in, final double money) {
            // TODO Auto-generated method stub
    
            //使用事务管理器模板进行事务控制
            tt.execute(new TransactionCallbackWithoutResult() {
    
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                    // TODO Auto-generated method stub
                    adi.outMoney(out, money);
                    //一个异常,使用了事务控制,在出现了异常之后,事务会回滚
                    int i = 1 / 0;
                    adi.inMoney(in, money);
                }
            });
    
        }
    
    }

    由于是面向接口编程,这里我只贴出了service层的实现,使用了自动扫描机制(扫描类、属性上的注解@Component、@Autowired),transfer方法是实现转账的方法,首先从一个账户转出,然后转入另一个账户,使用事务管理器模板的execute方法,需要一个TransactionCallBack的实例,这里使用匿名内部类的方式,把要执行的方法放在doInTransactionWithoutResult中执行,保证了事务的控制。

    使用这种方式可以保证事务控制,但是在实际开发过程当中这种方式对代码的改动太大,不符合低侵入开发原则,所有这种方式在开发中几乎很少用到,用的最多的是声明式的事务控制。

    二、声明式事务控制

    声明式事务控制又分为三种方式,一、基于TransactionProxyFactoryBean代理的声明式事务控制;二、使用AOP的声明式事务控制;三、基于@Transactional注解的声明式事务控制。

    1、基于TransactionProxyFactoryBean的声明式事务控制

    TransactionProxyFactoryBean是事务的代理类,spring会为目标类生成一个代理,具体的配置如下,

    <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <!--配置业务层代理-->
        <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
            <property name="target" ref="accountServiceImpl"></property>
            <property name="transactionManager" ref="transactionManager"></property>
            <property name="transactionAttributes">
                <props>
                    <prop key="transfer"></prop>
                </props>
            </property>
        </bean>

    这里只贴出了事务管理器和业务层代理的配置,剩余的数据源和业务类的配置可以执行配置,前面说到无论使用哪种方式配置事务管理,都需要使用事务管理器。重点看业务层代理,配置的class属性为TransactionProxyFactoryBean,需要配置三个属性:target(要代理的具体业务层实现类)、transactionManager(事务管理器)、transactionAttributes(要拦截的业务层方法)。配置完成之后,便可以进行测试,测试代码如下,

    ApplicationContext ac=getApplicationContext();
            AccountServiceIter asi=(AccountServiceIter)ac.getBean("accountServiceProxy");
            asi.transfer("aa", "cc", 10d);

    通过getApplicationContext()方法获得了ApplicationContext实例,然后获得accountServiceProxy的实例,这里获得的不是AccountServiceImpl的实例而是代理的实例对象,因为使用代理,代理了实际的业务类,所有这里不能再使用实际的类而应是代理类。

    使用这种方式的需要为每一个需要使用事务的业务类配置一个代理比教麻烦,所以在开发过程中这种方式几乎不用。

    2、使用AOP的声明式事务控制

    这种方式是在开发过程中使用的比较多的一种,配置如下,

    <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <!--配置事务增强-->
        <tx:advice id="advicer" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="transfer*" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
        <!--配置切点、事务通知-->
        <aop:config>
            <aop:pointcut id="myPointcut" expression="execution(* com.cn.study.day555.service.inter.impl.*.*(..))"/>
            <aop:advisor advice-ref="advicer" pointcut-ref="myPointcut"/>
        </aop:config>

    配置了事务增强<tx:advice>配置对要增强的方法的事务的传播行为等,配置<aop:config>配置切点和对应的事务通知,这样就完成了AOP的声明式事务控制。

    3、基于@Transactional注解

    使用@Transactional注解需要再配置文件中开启对这个注解的扫描:<tx:annotation-driven transaction-manager="transactionManager" />,引用了事务管理器,然后就可以使用@Transactional注解,此注解可以使用在类上,也可以使用在方法上,使用在类上即对此类的所有方法都起作用,使用在方法上则表示对单个方法起作用,还可以配置一些属性,放在另一篇文章中进行解释。

    通过对以上四种配置事务的方式的描述,其中声明式方式中的第二种方式使用比较普通,对代码的侵入比较小,第三种因为配置简单,也比较常用,但是需要在业务类或方法上加@Transcational注解,对代码有一定的侵入。

    有不正之处欢迎指出,谢谢!

  • 相关阅读:
    Dockfile 使用 非root 用户运行容器
    Latex 小记
    Tmux 小技巧
    配置本地 overleaf
    arm板载ubuntu18.04系统安装QT4.8.7
    移植32位QT程序到ubuntu18.04
    TLS1.0禁用问题
    TLS1.0禁用问题
    Delphi程序“自杀”的有效办法
    Delphi7程序出现“EOSError code8-存储不足”问题的分析与解决
  • 原文地址:https://www.cnblogs.com/teach/p/5823574.html
Copyright © 2011-2022 走看看