zoukankan      html  css  js  c++  java
  • JavaWeb_(Spring框架)Spring中的aop事务

    1、事务相关知识

      a)什么是事务:把多条数据库操作捆绑到一起执行,要么都成功,要么都失败;

      b)事务的原则ACID:

        i.原子性:事务包含的所有操作,要么全部成功,要么全部失败回滚,成功全部应用到数据库,失败不能对数据库有任何影响;

        ii.一致性:事务在执行前和执行后必须一致;例如A和B一共有100块钱,无论A、B之间如何转账,他们的钱始终相加都是100;

        iii.隔离性:多用户并发访问同一张表时,数据库为每一个用户开启新的事务,该事务不能被其他事务所影响,相互有隔离;  

        iv.持久性:一个事务一旦提交,则对数据库中数据的改变是永久的,即便系统故障也不会丢失;

      c)并发可能引起的问题:

        i.脏读:一个事务读取到另一个事务未提交的数据;

        ii.不可重复读:一个事务读取到另一个事务已提交(Update操作)的数据,导致前后读取不一致; 

        iii.幻读(虚读):一个事务中读取到别的事务插入(Insert操作)的数据,导致前后读取不一致;

      d)事务的隔离级别:根据实际情况选择;

        i.Serializable串行化:可避免脏读、不可重复读和幻读;

        ii.Repeatable read可重复读:可避免脏读、不可重复读;(MySql默认值)

        iii.Read committed读已提交:可避免脏读;

        iv.Read uncommitted读未提交:任何情况都无法保证;

    2、Spring-aop事务

      a)事务基本操作:打开事务、提交事务、回滚事务;

      b)Spring中利用接口来管理不同框架的事务操作;

        i.通过实现PlatformTransactionManager接口支持不同的框架完成各自的事务处理;

        ii.为不同平台提供对应的事务管理器的实现:JDBC&Mybatis:DataSourceTransactionManager;

      c)Spring-aop事务通过配置事务的隔离级别、事务传播行为、是否只读来操作;

        i.隔离级别:串行化、可重复读、读已提交、读未提交;

        ii.是否只读:

          1.true:不可改变数据库中的数据,查询操作推荐,

          2.false:可以改变数据库数据;

        iii.事务传播行为:事务方法嵌套调用的规则:

          xService.x(); -> yService.y();

          1.REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置;

          2.REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务;

          3.SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行;

          4.NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起(暂停);

          5.MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常;

          6.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常;

          7.NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作。

    3、Spring-aop事务 – 从麻烦的事务代码中走出之xml配置版aop事务;

      a)使用经典的转账案例进行测试,准备数据:bean、service、dao;

        数据库account表中添加三个字段,并添加两条叫数据

      

       

      实现老王(A)向老李(B)转账操作,AccountServiceImpl.java中实现转账操作

      @Override
      public void transferAccounts() {
        //转账逻辑
            
        //先从A账户扣款
        ad.subMoney(1,50d);
            
        //再给B账户加款
        ad.addMoney(2,50d);
        }

      

       

    package com.Gary.bean;
    
    public class Account {
    
        private Integer id;
        private String name;
        private Double money;
        
        
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Double getMoney() {
            return money;
        }
        public void setMoney(Double money) {
            this.money = money;
        }
        
        
        
    }
    Account.java
    package com.Gary.dao;
    
    public interface AccountDao {
    
        //扣款
        void subMoney(Integer i, Double d);
    
        //加款
        void addMoney(Integer i, Double d);
    
    }
    AccountDao.java
    package com.Gary.dao;
    
    import org.springframework.jdbc.core.support.JdbcDaoSupport;
    
    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    
        @Override
        public void subMoney(Integer id, Double money) {
            String sql = "update account set money = money - ? where id = ?";
            getJdbcTemplate().update(sql,money,id);
        }
    
        @Override
        public void addMoney(Integer id, Double money) {
            String sql = "update account set money = money + ? where id = ?";
            getJdbcTemplate().update(sql,money,id);
    
        }
    
    }
    AccountDaoImpl.java
    package com.Gary.service;
    
    public interface AccountService {
    
        //转账接口
        void transferAccounts();
        
    }
    AccountService.java
    package com.Gary.service;
    
    import com.Gary.bean.Account;
    import com.Gary.dao.AccountDao;
    
    public class AccountServiceImpl implements AccountService{
    
        //账户dao
        private AccountDao ad;
        
        @Override
        public void transferAccounts() {
            //转账逻辑
            
            //先从A账户扣款
            ad.subMoney(1,50d);
            
            //再给B账户加款
            ad.addMoney(2,50d);
            
        }
    
        public void setAd(AccountDao ad) {
            this.ad = ad;
        }
        
        
        
    }
    AccountServiceImpl.java
    package com.Gary.test;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.Gary.service.AccountService;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class TxTest {
        
        @Resource(name="accountService")
        private AccountService as;
        
        @Test
        public void Test1() {
            as.transferAccounts();
        }
        
    }
    TxTest.java
    <?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
    
        <!-- 依赖关系 dao -> -> dataSource -->
        <!-- 读取配置文件 -->
        <context:property-placeholder location="db.properties" />
    
        <!-- 配置 dataSource -->
        <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driverClass}" />
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
            <property name="user" value="${jdbc.user}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <!-- dao -->
        <bean name="accountDao" class="com.Gary.dao.AccountDaoImpl">
            <property name="dataSource" ref="dataSource" />
        </bean>
    
        <!-- service -->
        <bean name="accountService" class="com.Gary.service.AccountServiceImpl">
            <property name="ad" ref="accountDao" />
        </bean>
    
    
    </beans>
    applicationContext.xml
    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_spring
    jdbc.user=root
    jdbc.password=123456
    db.properties

      当AccountServiceImpl.java中转账操作异常时,比如1/0

        @Override
        public void transferAccounts() {
            //转账逻辑
            
            //先从A账户扣款
            ad.subMoney(1,50d);
            
            int a = 1/0;
            
            //再给B账户加款
            ad.addMoney(2,50d);
            
        }

      可以发现,老王(A)向老李(B)转账了50块钱,但因为中途发生了异常,老李没有收到,可数据库中真实扣了50块钱

    package com.Gary.service;
    
    import com.Gary.bean.Account;
    import com.Gary.dao.AccountDao;
    
    public class AccountServiceImpl implements AccountService{
    
        //账户dao
        private AccountDao ad;
        
        @Override
        public void transferAccounts() {
            //转账逻辑
            
            //先从A账户扣款
            ad.subMoney(1,50d);
            
            //异常
            int a = 1/0;
            
            //再给B账户加款
            ad.addMoney(2,50d);
            
        }
    
        public void setAd(AccountDao ad) {
            this.ad = ad;
        }
        
        
        
    }
    AccountServiceImpl.java

      b)使用事务需要额外导入tx包和tx约束;

      

      c)配置事务核心管理器: DataSourceTransactionManager;

        <!-- 配置事务核心管理器 不同平台不一样 -->
        <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>

      d)配置事务通知 tx:Advice;

        <!-- 事务通知 -->
        <tx:advice id="txAdivce" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="transferAccounts" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
            </tx:attributes>
        </tx:advice>

      e)配置aop;

        <!-- 配置aop -->
        <aop:config>
            <aop:pointcut expression="execution(* com.Gary.service.*ServiceImpl.*(..))" id="txPc"/>
            <aop:advisor advice-ref="txAdivce" pointcut-ref="txPc"/>
        </aop:config>
    <?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
    
        <!-- 依赖关系 dao -> -> dataSource -->
        <!-- 读取配置文件 -->
        <context:property-placeholder location="db.properties" />
    
        <!-- 配置 dataSource -->
        <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driverClass}" />
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
            <property name="user" value="${jdbc.user}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <!-- dao -->
        <bean name="accountDao" class="com.Gary.dao.AccountDaoImpl">
            <property name="dataSource" ref="dataSource" />
        </bean>
    
        <!-- service -->
        <bean name="accountService" class="com.Gary.service.AccountServiceImpl">
            <property name="ad" ref="accountDao" />
        </bean>
    
        <!-- 配置事务核心管理器 不同平台不一样 -->
        <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- 事务通知 -->
        <tx:advice id="txAdivce" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="transferAccounts" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
            </tx:attributes>
        </tx:advice>
        
        <!-- 配置aop -->
        <aop:config>
            <aop:pointcut expression="execution(* com.Gary.service.*ServiceImpl.*(..))" id="txPc"/>
            <aop:advisor advice-ref="txAdivce" pointcut-ref="txPc"/>
        </aop:config>
    
    </beans>
    applicationContext.xml

      

      测试后发现,当A向B转账过程发生异常后,A向B转账的50块钱会进行回滚。

      xml配置版事务

      如果存在多个事务,可以在applicationContext.xml下进行注解配置

        <!-- 事务通知 -->
        <tx:advice id="txAdivce" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="transferAccounts" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="save*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="delete*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="update*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="select*" isolation="DEFAULT"  propagation="REQUIRED" read-only="true"/>
            </tx:attributes>
        </tx:advice>

      注解版事务

      需要在aplicationContext.xml中开启注解事务

        <!-- 开启注解事务 -->
        <tx:annotation-driven/>

      添加事务,只需要在方法上使用@Transactional注解

      给转账事务添加注解

        @Override
        @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,readOnly = false)
        public void transferAccounts() {
            //转账逻辑
            
            //先从A账户扣款
            ad.subMoney(1,50d);
            
            //异常
            int a = 1/0;
            
            //再给B账户加款
            ad.addMoney(2,50d);
            
        }
    package com.Gary.service;
    
    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.Gary.dao.AccountDao;
    
    public class AccountServiceImpl implements AccountService{
    
        //账户dao
        private AccountDao ad;
        
        @Override
        @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,readOnly = false)
        public void transferAccounts() {
            //转账逻辑
            
            //先从A账户扣款
            ad.subMoney(1,50d);
            
            //异常
            int a = 1/0;
            
            //再给B账户加款
            ad.addMoney(2,50d);
            
        }
    
        public void setAd(AccountDao ad) {
            this.ad = ad;
        }
        
        
        
    }
    AccountServiceImpl.java
    <?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
    
        <!-- 依赖关系 dao -> -> dataSource -->
        <!-- 读取配置文件 -->
        <context:property-placeholder location="db.properties" />
    
        <!-- 配置 dataSource -->
        <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driverClass}" />
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
            <property name="user" value="${jdbc.user}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <!-- dao -->
        <bean name="accountDao" class="com.Gary.dao.AccountDaoImpl">
            <property name="dataSource" ref="dataSource" />
        </bean>
    
        <!-- service -->
        <bean name="accountService" class="com.Gary.service.AccountServiceImpl">
            <property name="ad" ref="accountDao" />
        </bean>
    
        <!-- 配置事务核心管理器 不同平台不一样 -->
        <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- 事务通知 -->
        <tx:advice id="txAdivce" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="transferAccounts" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="save*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="delete*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="update*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="select*" isolation="DEFAULT"  propagation="REQUIRED" read-only="true"/>
            </tx:attributes>
        </tx:advice>
        
        <!-- 配置aop -->
        <aop:config>
            <aop:pointcut expression="execution(* com.Gary.service.*ServiceImpl.*(..))" id="txPc"/>
            <aop:advisor advice-ref="txAdivce" pointcut-ref="txPc"/>
        </aop:config>
        
        <!-- 开启注解事务 -->
        <tx:annotation-driven/>
    
    </beans>
    applicationContext.xml
  • 相关阅读:
    三、 复杂对象类型的WebService
    Axis2.x WebService开发指南目录索引
    eclipse/MyEclipse 日期格式、注释日期格式、时区问题
    IE6、IE7、IE8的CSS、JS兼容
    一、CXF WebService准备工作
    十二、用Axis操作 Header头部信息
    六、 跨多个WebService管理Session
    jQuery autocomplete 自扩展插件、自动补全示例
    二、Axis2的简单WebService示例
    六、传递、返回复杂类型的对象
  • 原文地址:https://www.cnblogs.com/1138720556Gary/p/12011087.html
Copyright © 2011-2022 走看看