当前位置: 首页 > news >正文

Spring事务管理之几种方式实现事务(转)

一:事务认识

  大家所了解的事务Transaction,它是一些列严密操作动作,要么都操作完成,要么都回滚撤销。Spring事务管理基于底层数据库本身的事务处理机制。数据库事务的基础,是掌握Spring事务管理的基础。这篇总结下Spring事务。

  事务具备ACID四种特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写。 

  (1)原子性(Atomicity)
    事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
  (2)一致性(Consistency)
    事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
  (3)隔离性(Isolation)
    指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
  (4)持久性(Durability)
    指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。


二:事务的传播机制

  详情看这个: 事务的传播机制

三:数据库的4种隔离级别

  详情看这个:  数据库的4种隔离级别


 提示:

  spring的事务传播行为是:REQUIRED

  mysql的默认的事务处理级别是:REPEATABLE-READ, 也就是可重复读

  Oracle的默认的事务处理级别是:READ COMMITTED, 也就是读已提交

可以通过更改数据库的默认事务处理级别


 四:事务几种实现方式 (编程式事务管理编程式事务管理) 

  (1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
  (2)基于 TransactionProxyFactoryBean的声明式事务管理
  (3)基于 @Transactional 的声明式事务管理
  (4)基于Aspectj AOP配置事务
 注:此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考。


 五:举例说明事务不同实现

  以用户购买股票为例
  新建用户对象、股票对象、以及dao、service层
/**
 * 账户对象
 *
 */
public class Account {
 
    private int accountid;
    private String name;
    private String balance;
    
    
    public int getAccountid() {
        return accountid;
    }
    public void setAccountid(int accountid) {
        this.accountid = accountid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getBalance() {
        return balance;
    }
    public void setBalance(String balance) {
        this.balance = balance;
    }
}
/**
 * 股票对象
 *
 */
public class Stock {
 
    private int stockid;
    private String name;
    private Integer count;
    
    public Stock() {
        super();
    }
     
    public Stock(int stockid, String name, Integer count) {
        super();
        this.stockid = stockid;
        this.name = name;
        this.count = count;
    }
 
    public int getStockid() {
        return stockid;
    }
 
    public void setStockid(int stockid) {
        this.stockid = stockid;
    }
 
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getCount() {
        return count;
    }
    public void setCount(Integer count) {
        this.count = count;
    }
    
}

  DAO层

public interface AccountDao {
 
    void addAccount(String name,double money);
    
    void updateAccount(String name,double money,boolean isbuy);
    
}
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
 
    @Override
    public void addAccount(String name, double money) {
        String sql = "insert account(name,balance) values(?,?);";
        this.getJdbcTemplate().update(sql,name,money);
        
    }
 
    @Override
    public void updateAccount(String name, double money, boolean isbuy) {
        String sql = "update account set balance=balance+? where name=?";
        if(isbuy)
            sql = "update account set balance=balance-? where name=?";
        this.getJdbcTemplate().update(sql, money,name);
    }
    
}
public interface StockDao {
    
    void addStock(String sname,int count);
    
    void updateStock(String sname,int count,boolean isbuy);
 
}
public class StockDaoImpl extends JdbcDaoSupport implements StockDao {
 
    @Override
    public void addStock(String sname, int count) {
        String sql = "insert into stock(name,count) values(?,?)";
        this.getJdbcTemplate().update(sql,sname,count);
    }
 
    @Override
    public void updateStock(String sname, int count, boolean isbuy) {
        String sql = "update stock set count = count-? where name = ?";
        if(isbuy)
            sql = "update stock set count = count+? where name = ?";
        this.getJdbcTemplate().update(sql, count,sname);
    }
    
}

  Service层

public interface BuyStockService {
 
    public void addAccount(String accountname, double money);
    
    public void addStock(String stockname, int amount);
    
    public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException;
    
}
public class BuyStockServiceImpl implements BuyStockService{
    
    private AccountDao accountDao;
    private StockDao stockDao;
    
    @Override
    public void addAccount(String accountname, double money) {
        accountDao.addAccount(accountname,money);
    }
 
    @Override
    public void addStock(String stockname, int amount) {
        stockDao.addStock(stockname,amount);
    }
 
    @Override
    public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException {
        boolean isBuy = true;
        accountDao.updateAccount(accountname, money, isBuy);
        if(isBuy==true){
            throw new BuyStockException("购买股票发生异常");
        }
            stockDao.updateStock(stockname, amount, isBuy);
    }
 
    public AccountDao getAccountDao() {
        return accountDao;
    }
 
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
 
    public StockDao getStockDao() {
        return stockDao;
    }
 
    public void setStockDao(StockDao stockDao) {
        this.stockDao = stockDao;
    }
    
}

  自定义异常类

public class BuyStockException extends Exception {
 
    public BuyStockException() {
        super();
    }
 
    public BuyStockException(String message) {
        super(message);
    }
 
}

(1)基于 TransactionProxyFactoryBean的声明式事务管理

<?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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-aop-4.2.xsd
        ">
    
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 注册数据源 C3P0 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  >
         <property name="driverClass" value="${jdbc.driverClass}"></property>
         <property name="jdbcUrl"  value="${jdbc.url}"></property>
         <property name="user"  value="${jdbc.username}"></property>
         <property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <bean id="accountDao" class="transaction.test2.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="stockDao" class="transaction.test2.dao.StockDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="buyStockService" class="transaction.test2.service.BuyStockServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="stockDao" ref="stockDao"></property>
    </bean>
    
    
    <!-- 事务管理器 -->
    
    <bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 事务代理工厂 -->
    <!-- 生成事务代理对象 -->
    <bean id="serviceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager" ref="myTracnsactionManager"></property>
        <property name="target" ref="buyStockService"></property>
        <property name="transactionAttributes">
            <props>
                <!-- 主要 key 是方法   
                    ISOLATION_DEFAULT  事务的隔离级别
                    PROPAGATION_REQUIRED  传播行为
                -->
                <prop key="add*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop>
                <!-- -Exception 表示发生指定异常回滚,+Exception 表示发生指定异常提交 -->
                <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-BuyStockException</prop>
            </props>
        </property>
        
    </bean>
    
    
</beans>  

  测试入口

public static void main(String[] args) {
        String resouce = "transaction/test2/applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(resouce);
        BuyStockService buyStockService =  (BuyStockService) applicationContext.getBean("serviceProxy");
 
//        buyStockService.openAccount("小郑", 5000);
        
//        buyStockService.openStock("知晓科技", 0);
        
        try {
            buyStockService.buyStock("小郑", 1000, "知晓科技", 100);
        } catch (BuyStockException e) {
            e.printStackTrace();
        }
        
    }

  发生异常账户金额不能减,股票不能增加

(2)基于 @Transactional 的声明式事务管理

   其他类不做改变,只改变购买股票接口实现类和配置文件

public class BuyStockServiceImpl implements BuyStockService{
 
    private AccountDao accountDao;
    private StockDao stockDao;
    
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
    @Override
    public void addAccount(String accountname, double money) {
        accountDao.addAccount(accountname,money);
        
    }
 
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
    @Override
    public void addStock(String stockname, int amount) {
        stockDao.addStock(stockname,amount);
        
    }
 
    public BuyStockServiceImpl() {
        // TODO Auto-generated constructor stub
    }
    
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,rollbackFor=BuyStockException.class)
    @Override
    public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException {
        boolean isBuy = true;
        accountDao.updateAccount(accountname, money, isBuy);
        if(isBuy==true){
            throw new BuyStockException("购买股票发生异常");
        }
            stockDao.updateStock(stockname, amount, isBuy);
        
    }
 
    public AccountDao getAccountDao() {
        return accountDao;
    }
 
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
 
    public StockDao getStockDao() {
        return stockDao;
    }
 
    public void setStockDao(StockDao stockDao) {
        this.stockDao = stockDao;
    }
    
}
<context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 注册数据源 C3P0 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  >
         <property name="driverClass" value="${jdbc.driverClass}"></property>
         <property name="jdbcUrl"  value="${jdbc.url}"></property>
         <property name="user"  value="${jdbc.username}"></property>
         <property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <bean id="accountDao" class="transaction.test3.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="stockDao" class="transaction.test3.dao.StockDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="buyStockService" class="transaction.test3.service.BuyStockServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="stockDao" ref="stockDao"></property>
    </bean>
    
    
    <!-- 事务管理器 -->
    <bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 启用事务注解 -->
    <tx:annotation-driven transaction-manager="myTracnsactionManager"/>

  可以看出,使用@Transactional注解的方式配置文件要简单的多,将事务交给事务注解驱动。它有个缺陷是他会把所有的连接点都作为切点将事务织入进去,显然只需要在buyStock()方法织入事务即可。下面看看最后一种实现,它就可以精准的织入到指定的连接点

知识点:

事务超时:@Transactional(timeout = 60)

  如果用这个注解描述一个方法的话,线程已经跑到方法里面,如果已经过去60秒了还没跑完这个方法并且线程在这个方法中的后面还有涉及到对数据库的增删改查操作时会报事务超时错误(会回滚)。
  如果已经过去60秒了还没跑完但是后面已经没有涉及到对数据库的增删改查操作,那么这时不会报事务超时错误(不会回滚)。
回滚:
  Spring管理事务默认回滚的异常是什么? 
  答案是 RuntimeException或者Error。 
  注意:如果事务在try{}catch(Exception e){e.printStackTrace();}中跑,并且catch中只是打印e的话,那么事务不会rollback。因为异常被catch掉了,框架不知道发生了异常。
  如果想要rollback,可以加上rollbackFor=Exception.class,然后:
    ①在方法上添加 throws  Exception,将方法中出现的异常抛出给spring事务,
    ②去掉方法体中的try catch
    ③catch (Exception e) {  throw e;}继续向上抛,目的是让spring事务捕获这个异常。
      rollbackFor=Exception.class,catch(){
           throw new RunTimeException();
      }
  如果不加rollbackFor=Exception.class,抛出 new Exception() 是不会回滚的。Spring源码如下:
    public  boolean rollbackOn(Throwable ex) { 
          return (ex  instanceof RuntimeException || ex  instanceof Error);
    } 
  如果是RuntimeException或Error的话,就返回True,表示要回滚,否则返回False,表示不回滚。
  只有spring事务捕获到Exception异常后,@Transactional(rollbackFor=Exception.class),才会起到应有的作用;catch (Exception e) {            e.printStackTrace();        }这句是捕获try中出现的Exception然后将异常信息打印出来,仅仅是打印出来,然后什么也没干。

  @Transactional(timeout = 60,rollbackFor=Exception.class)与rollbackFor=Exception.class的作用是
    1 让checked例外也回滚:在整个方法前加上 @Transactional( rollbackFor=Exception.class)
    2 让unchecked例外不回滚: @Transactional( notRollbackFor=RunTimeException.class)
  checked Unchecked exception是运行时错误。
 

 (3)基于Aspectj AOP配置事务

public class BuyStockServiceImpl implements BuyStockService{
 
    private AccountDao accountDao;
    private StockDao stockDao;
    
    @Override
    public void addAccount(String accountname, double money) {
        accountDao.addAccount(accountname,money);
        
    }
 
    @Override
    public void addStock(String stockname, int amount) {
        stockDao.addStock(stockname,amount);
        
    }
 
    public BuyStockServiceImpl() {
        // TODO Auto-generated constructor stub
    }
    
    @Override
    public void buyStock(String accountname, double money, String stockname, int amount) throws BuyStockException {
        boolean isBuy = true;
        accountDao.updateAccount(accountname, money, isBuy);
        if(isBuy==true){
            throw new BuyStockException("购买股票发生异常");
        }
            stockDao.updateStock(stockname, amount, isBuy);
        
    }
 
    public AccountDao getAccountDao() {
        return accountDao;
    }
 
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
 
    public StockDao getStockDao() {
        return stockDao;
    }
 
    public void setStockDao(StockDao stockDao) {
        this.stockDao = stockDao;
    }
    
}
<context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 注册数据源 C3P0 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  >
         <property name="driverClass" value="${jdbc.driverClass}"></property>
         <property name="jdbcUrl"  value="${jdbc.url}"></property>
         <property name="user"  value="${jdbc.username}"></property>
         <property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <bean id="accountDao" class="transaction.test4.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="stockDao" class="transaction.test4.dao.StockDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="buyStockService" class="transaction.test4.service.BuyStockServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="stockDao" ref="stockDao"></property>
    </bean>
    
    
    <!-- 事务管理器 -->
    <bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <tx:advice id="txAdvice" transaction-manager="myTracnsactionManager">
        <tx:attributes>
            <!-- 为连接点指定事务属性 -->
            <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED"/>
            <tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED" rollback-for="BuyStockException"/>
        </tx:attributes>
    </tx:advice>
    
    <aop:config>
        <!-- 切入点配置 -->
        <aop:pointcut expression="execution(* *..service.*.*(..))" id="point"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="point"/>
    </aop:config>

  执行接口与其他方式一样。

 

转自:https://blog.csdn.net/chinacr07/article/details/78817449

转载于:https://www.cnblogs.com/myseries/p/10834172.html

相关文章:

  • 【转】np.random.random()函数 参数用法以及numpy.random系列函数大全
  • 从零开始手写一个优化版React脚手架
  • JavaWeb学习笔记(十二)--Session案例
  • 实验:basic验证,组验证
  • 携程、阿里、京东、腾讯iOS春招面试过程以及面试题总结!
  • 003-软件质量模型的6大特性27个子特性(转)
  • 使用SpringSession管理分布式会话时遇到的反序列化问题
  • c语言程序设计第1章
  • 计算机的三大原则
  • Java并发之AQS详解
  • htaccess隐藏index.php,301重定向等等..
  • CF241B Friends
  • Git学习总结——简单易懂的教程
  • 整理收集的一些常用java工具类
  • vue+express+mysql +node项目搭建
  • JavaScript 如何正确处理 Unicode 编码问题!
  • [ JavaScript ] 数据结构与算法 —— 链表
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • Bootstrap JS插件Alert源码分析
  • CentOS7简单部署NFS
  • javascript从右向左截取指定位数字符的3种方法
  • Java基本数据类型之Number
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • Python十分钟制作属于你自己的个性logo
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • socket.io+express实现聊天室的思考(三)
  • Webpack 4x 之路 ( 四 )
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 回顾 Swift 多平台移植进度 #2
  • 普通函数和构造函数的区别
  • 在Docker Swarm上部署Apache Storm:第1部分
  • - 转 Ext2.0 form使用实例
  • 【干货分享】dos命令大全
  • ​插件化DPI在商用WIFI中的价值
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • (day6) 319. 灯泡开关
  • (Forward) Music Player: From UI Proposal to Code
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (附源码)ssm码农论坛 毕业设计 231126
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • *** 2003
  • .apk 成为历史!
  • .axf 转化 .bin文件 的方法
  • .cn根服务器被攻击之后
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET Core中的去虚
  • .net framework 4.0中如何 输出 form 的name属性。
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .NET使用存储过程实现对数据库的增删改查
  • .NET是什么
  • .net专家(张羿专栏)
  • [ C++ ] STL---stack与queue
  • [ IOS ] iOS-控制器View的创建和生命周期