• Spring的事务管理


    什么是事务?

    l  事务:逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败。

    理解事务之前,先讲一个你日常生活中最常干的事:转账。 
    比如你给朋友转账1000块钱,大体有两个步骤:首先输入密码金额,你的银行卡扣掉1000元钱;然后你朋友账上增加1000元钱。这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是转账失败,你朋友没收到1000元的话,你将会损失1000元;如果银行卡扣钱失败但是你朋友账户却增加了1000块,那么银行将损失1000元。所以,如果一个步骤成功另一个步骤失败对双方都不是好事,如果不管哪一个步骤失败了以后,整个转账过程都能回滚,也就是完全取消所有操作的话,这对双方都是极好的。 
    事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。 
    在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。 

    事务有四个特性:ACID

    l  原子性(Atomicity):事务不可分割

    l  一致性(Consistency):事务执行前后数据完整性保持一致

    l  隔离性(Isolation):一个事务的执行不应该受到其他事务的干扰

    l  持久性(Durability):一旦事务结束,数据就持久化到数据库

    Spring的事务管理的API

    PlatformTransactionManager:平台事务管理器

    •平台事务管理器:接口,是Spring用于管理事务的真正的对象。

    ♦DataSourceTransactionManager  :底层使用JDBC管理事务

    ♦ HibernateTransactionManager       :底层使用Hibernate管理事务

    TransactionDefinition   :事务定义信息

    •事务定义:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读

    TransactionStatus:事务的状态

    •事务状态:用于记录在事务管理过程中,事务的状态的对象。

    事务管理的API的关系:

    Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理,在事务管理过程中,

    产生各种状态,将这些状态的信息记录到事务状态的对象中。

    Spring的事务的传播行为

    * 事务的传播行为主要用来解决业务层方法相互调用的问题。

    l  Spring中提供了七种事务的传播行为(可分为三类):

    ♦ 保证多个操作在同一个事务中

    •  PROPAGATION_REQUIRED    :默认值,如果A方法有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来

    •  PROPAGATION_SUPPORTS    :支持事务,如果A方法有事务,使用A中的事务。如果A没有事务,不使用事务。

    •        PROPAGATION_MANDATORY    :如果A方法有事务,使用A中的事务。如果A没有事务,抛出异常。

    ♦  保证多个操作不在同一个事务中

    •  PROPAGATION_REQUIRES_NEW    :如果A有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。

    •  PROPAGATION_NOT_SUPPORTED   :如果A有事务,将A的事务挂起。不使用事务管理。

    •  PROPAGATION_NEVER       :如果A有事务,报异常。

    ♦  嵌套式事务

    •  PROPAGATION_NESTED        :嵌套事务,如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。

    接着模拟一下上述转账这个过程的事务管理

    先搭建这个事物管理环境:

    创建Service的接口和实现类

    /**
     * 转账的业务层的实现类
     * @author jt
     *
     */
    public class AccountServiceImpl implements AccountService {
    
        // 注入DAO:
        private AccountDao accountDao;
        
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
        
        @Override
        /**
         * from:转出账号
         * to:转入账号
         * money:转账金额
         */
        public void transfer( String from,  String to,  Double money) {
            
            accountDao.outMoney(from, money);
    //        int d = 1/0;
    //             上一行是模拟转账过程出问题,这里先暂时注释掉。
            accountDao.inMoney(to, money);
            
        }
    
    }

    创建DAO的接口和实现类

    /**
     * 转账的DAO的实现类
     * @author jt
     *
     */
    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
      //这里继承了JdbcDaoSupport是模版类,就省的再进行模版注入了
        @Override
        public void outMoney(String from, Double money) {
            this.getJdbcTemplate().update("update account set money = money - ? where name = ?", money,from);
        }
    
        @Override
        public void inMoney(String to, Double money) {
            this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money ,to);
        }
    
    }

    配置Service和DAO:交给Spring管理

    <!-- 配置Service============= -->
        <bean id="accountService" class="com.xk.xx.xx.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"/>
        </bean>
        
        <!-- 配置DAO================= -->
        <bean id="accountDao" class="com.xk.xx.xx.AccountDaoImpl">
            <property name="dataSource" ref="dataSource"/>
        </bean>

     配置连接池和JDBC的模板

    <!-- 配置连接池 -->
        <!-- 第二种方式通过context标签引入的 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
        
        <!-- 配置C3P0连接池=============================== -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driverClass}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>

    这是运行测试类之前的数据库数据。

    编写测试类:

    /**
     * 测试转账的环境
     * @author jt
     *
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringDemo1 {
    
        @Resource(name="accountService")
        private AccountService accountService;
        
        @Test
        public void demo1(){
            accountService.transfer("甲", "丁", 1000d);
        }
    }

    运行测试类后数据库数据如下:

    转账正常进行。

    接着将数据恢复转账之前,然后将之前AccountServiceImpl中提到的注释打开,模拟转账出现问题。然后运行测试类

    会发现,甲的钱被扣了1000,但是丁却没有收到

    这就是开头所举例子。

    那么接下来用事物管理来解决这种问题的发生。

    首先配置事务管理器

    <!-- 配置事务管理器=============================== -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>

    配置增强

    <!-- 配置事务的增强=============================== -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <!-- 事务管理的规则 -->
                <!-- <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"/>
                <tx:method name="update*" propagation="REQUIRED"/>
                <tx:method name="delete*" propagation="REQUIRED"/>
                <tx:method name="find*" read-only="true"/> -->
                <tx:method name="*" propagation="REQUIRED" read-only="false"/>
            </tx:attributes>
        </tx:advice>

    AOP的配置

    <!-- aop的配置 -->
        <aop:config>
            <aop:pointcut expression="execution(* com.xk.xx.xx.AccountServiceImpl.*(..))" id="pointcut1"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
        </aop:config>

     再次运行测试类:

    发现转账失败,但是没有扣甲的钱,丁的钱也没有增加

    因为转账一旦出现问题,整个过程都会回滚。

    这就是事务管理的作用之一。


    上面采用的声明式事务管理是用xml配的。采用的是AOP思想。

    也可以选择用注解的方式:

    这里,配置文件中,只需配置事务管理器然后开启注解事务即可

    <!-- 配置事务管理器=============================== -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    <!-- 开启注解事务=============== -->
        <tx:annotation-driven transaction-manager="transactionManager"/>

    然后在业务层添加注解

    @Transactional还有许多属性,根据需要添加即可

    注解的方式,配置少些,但是每次必须自行给需要的业务层添加注解。

  • 相关阅读:
    杂记:高僧的炒股境界
    在Windows 7中使用tsmmc远程桌面
    VS2010初体验
    code4fun: one service,one config
    WCF进阶:将编码后的字节流压缩传输
    有高手想换工作的么?
    code4fun:host wcf service just in time
    evey的几张鼠绘
    说说WCF Rest
    外包一类似联众room的项目
  • 原文地址:https://www.cnblogs.com/xk920/p/9805409.html
Copyright © 2020-2023  润新知