一:事务认识

  大家所了解的事务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

Spring事务管理之几种方式实现事务(转)的更多相关文章

  1. Spring事务管理之几种方式实现事务

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

  2. 事务管理(下) 配置spring事务管理的几种方式(声明式事务)

    配置spring事务管理的几种方式(声明式事务) 概要: Spring对编程式事务的支持与EJB有很大的区别.不像EJB和Java事务API(Java Transaction API, JTA)耦合在 ...

  3. Spring事务管理的四种方式(以银行转账为例)

    Spring事务管理的四种方式(以银行转账为例) 一.事务的作用 将若干的数据库操作作为一个整体控制,一起成功或一起失败.   原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不 ...

  4. Spring事务管理的三种方式

    一 .第一种:全注解声明式事务 Xml代码 复制代码 收藏代码 .<?xml version="1.0" encoding="UTF-8"?> .& ...

  5. 配置spring事务管理的几种方式(声明式事务)

    Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource.TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分. DataSo ...

  6. Spring中事务管理的两种方式

    spring支持编程式事务管理和声明式事务管理两种方式. 编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager.对于编程式事务 ...

  7. Spring事务管理的两种方式

    参考文档: http://www.iteye.com/topic/1123347 http://blog.csdn.net/lcj8/article/details/2835432 PS:好像还是tx ...

  8. 事务有哪些特性?spring的事务管理有几种方式实现,如何实现?

    特性:1.原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做要么全不做 2.一致性:数据不会因为事务的执行而遭到破坏 3.隔离性:一个事物的执行,不受其他事务的干扰,即并发执行的事 ...

  9. Spring加载applicationContext.xml实现spring容器管理的几种方式

    package com.etc.test; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; i ...

随机推荐

  1. LA 4327 Parade(单调队列优化dp)

    题目链接: 题目大意(摘自刘汝佳<<算法竞赛入门经典--训练指南>>):F城是由n+1条横向路和m+1条竖向路组成.你的任务是从最南边的路走到最北边的路,使得走过的路上的高兴值 ...

  2. Eclipse Debug模式的开启与关闭问题简析_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 默认情况下,eclipse中右键debug,当运行到设置的断点时会自动跳到debug模式下.但由于我的eclipse环境 ...

  3. A1046

    n个节点围成一个环,每个节点之间的距离已知.输入n并给出n个节点的距离,输入m组节点编号(两个),求这两个节点编号间的最短距离. 1 建立dis[]数组,记录V1点到每一个点的顺时针距离,sum计算环 ...

  4. PHP高手干货分享:不能不看的50个细节!【PHP培训教程】

    兄弟连分享PHP高手干货:不能不看的50个细节 1.用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以 ...

  5. ajax +formdata ,后台为PHP 实现上传整个文件夹(只适合谷歌浏览器)带进度条

    PHP用超级全局变量数组$_FILES来记录文件上传相关信息的. 1.file_uploads=on/off 是否允许通过http方式上传文件 2.max_execution_time=30 允许脚本 ...

  6. codeforces 868C - Qualification Rounds(构造)

    原题链接:http://codeforces.com/problemset/problem/868/C 题意:有k个队伍参加比赛,比赛有n个预选的题目,有些队伍对已经事先知道了一些题目.问能不能选出若 ...

  7. codevs 1026 逃跑的拉尔夫 x

    1026 逃跑的拉尔夫  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold   题目描述 Description 年轻的拉尔夫开玩笑地从一个小镇上偷走了一辆车,但他 ...

  8. 通过java反射机制,修改年龄字段的值

    需求:将生日转为年龄 /** * 获取年龄值 */ public List getAgeInfo(List list) throws Exception { if (null == list || l ...

  9. Elasticsearch结构化搜索与查询

    Elasticsearch 的功能之一就是搜索,搜索主要分为两种类型,结构化搜索和全文搜索.结构化搜索是指有关查询那些具有内在结构数据的过程.比如日期.时间和数字都是结构化的:它们有精确的格式,我们可 ...

  10. [BZOJ2038]:[2009国家集训队]小Z的袜子(hose)(离线莫队)

    题目传送门 题目描述 作为一个生活散漫的人,小$Z$每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……具体来说,小$Z$把这 ...