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
  • 相关阅读:
    JS LeetCode 1423. 可获得的最大点数简单题解
    SpringBoot 学集 (第六章) Docker
    Linux 学记 (第三章)
    Linux 学记 (第二章)
    Linux 学记 (第一章)
    SpringBoot 学集 (第五章) Web开发续
    SpringBoot 学集 (第四章)Web开发
    SpringBoot 学集 (第三章) 日志框架
    SpringBoot 学集 (第二章) 配置文件
    SpringBoot 学集 (第一章)
  • 原文地址:https://www.cnblogs.com/1138720556Gary/p/12011087.html
Copyright © 2011-2022 走看看