Spring中的事务管理
事务简介:
	  事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性
	  事务就是一系列的动作,它们被当作一个单独的工作单元。这些动作要么全部完成,要么全部不起作用
	  事务的四个关键属性(ACID)
		    原子性(atomicity):事务室一个原子操作,有一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用
		    一致性(consistency):一旦所有事务动作完成,事务就被提交。数据和资源就处于一种满足业务规则的一致性状态中
		    隔离性(isolation):可能有许多事务会同时处理相同的数据,因此每个事物都应该与其他事务隔离开来,防止数据损坏
		    持久性(durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响。通常情况下,事务的结果被写到持久化存储器中
Spring中的事务管理
	  作为企业级应用程序框架,Spring在不同的事务管理API之上定义了一个抽象层。而应用程序开发人员不必了解底层的事务管理API,就可以使用Spring的事务管理机制。
Spring既支持编程式事务管理,也支持声明式的事务管理
	  编程式事务管理:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚,在编程式事务中,必须在每个业务操作中包含额外的事务管理代码
	  声明式事务管理:大多数情况下比编程式事务管理更好用。它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。事务管理作为一种横切关注点,可以通过AOP方法模块化。Spring通过Spring AOP框架支持声明式事务管理。
Spring事务的传播属性:
  当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
  事务的传播行为可以由传播属性指定。Spring定义了7种传播行为:
| 传播行为 | 含义 | 
| PROPAGATION_MANDATORY | 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 | 
| PROPAGATION_NESTED | 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务 | 
| PROPAGATION_NEVER | 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常 | 
| PROPAGATION_NOT_SUPPORTED | 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager | 
| PROPAGATION_REQUIRED | 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务 | 
| PROPAGATION_REQUIRED_NEW | 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager | 
| PROPAGATION_SUPPORTS | 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行 | 
其中PROPAGATION_REQUIRED为默认的传播属性
并发事务所导致的问题
	  在同一个应用程序或者不同应用程序中的多个事务在同一个数据集上并发执行时,可能会出现许多意外的问题。
	  并发事务所导致的问题可以分为以下三类:
		    脏读:脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
		    不可重复读:不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间更新了数据
		    幻读:幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录
-----------------------------------------------------代码示例------------------------------------------------------------------------------------
首先是数据库表:
包括book(isbn, book_name, price),account(username, balance),book_stock(isbn, stock)
然后是使用的类:
BookShopDao
 package com.yl.spring.tx;
 public interface BookShopDao {
     //根据书号获取书的单价
     public int findBookPriceByIsbn(String isbn);
     //更新书的库存,使书号对应的库存-1
     public void updateBookStock(String isbn);
     //更新用户的账户余额:使username的balcance-price
     public void updateUserAccount(String username, int price);
 }
BookShopDaoImpl
package com.yl.spring.tx; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository; @Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao { @Autowired
private JdbcTemplate JdbcTemplate; @Override
public int findBookPriceByIsbn(String isbn) {
String sql = "SELECT price FROM book WHERE isbn = ?"; return JdbcTemplate.queryForObject(sql, Integer.class, isbn);
} @Override
public void updateBookStock(String isbn) {
//检查书的库存是否足够,若不够,则抛出异常
String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?";
int stock = JdbcTemplate.queryForObject(sql2, Integer.class, isbn);
if (stock == 0) {
throw new BookStockException("库存不足!");
}
String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?";
JdbcTemplate.update(sql, isbn);
} @Override
public void updateUserAccount(String username, int price) {
//检查余额是否不足,若不足,则抛出异常
String sql2 = "SELECT balance FROM account WHERE username = ?";
int balance = JdbcTemplate.queryForObject(sql2, Integer.class, username);
if (balance < price) {
throw new UserAccountException("余额不足!");
}
String sql = "UPDATE account SET balance = balance - ? WHERE username = ?";
JdbcTemplate.update(sql, price, username);
} }
BookShopService
 package com.yl.spring.tx;
 public interface BookShopService {
     public void purchase(String username, String isbn);
 }
BookShopServiceImpl
package com.yl.spring.tx; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; @Service("bookShopService")
public class BookShopServiceImpl implements BookShopService { @Autowired
private BookShopDao bookShopDao; /**
* 1.添加事务注解
* 使用propagation 指定事务的传播行为,即当前的事务方法被另外一个事务方法调用时如何使用事务。
* 默认取值为REQUIRED,即使用调用方法的事务
* REQUIRES_NEW:使用自己的事务,调用的事务方法的事务被挂起。
*
* 2.使用isolation 指定事务的隔离级别,最常用的取值为READ_COMMITTED
* 3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚,也可以通过对应的属性进行设置。通常情况下,默认值即可。
* 4.使用readOnly 指定事务是否为只读。 表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务。若真的是一个只读取数据库值得方法,应设置readOnly=true
* 5.使用timeOut 指定强制回滚之前事务可以占用的时间。
*/
@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
readOnly=true, timeout=3)
@Override
public void purchase(String username, String isbn) {
//1.获取书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2.更新书的库存
bookShopDao.updateBookStock(isbn);
//3.更新用户余额
bookShopDao.updateUserAccount(username, price);;
} }
BookStockException
 package com.yl.spring.tx;
 public class BookStockException extends RuntimeException {
     /**
      *
      */
     private static final long serialVersionUID = 1L;
     public BookStockException() {
         super();
         // TODO Auto-generated constructor stub
     }
     public BookStockException(String arg0, Throwable arg1, boolean arg2,
             boolean arg3) {
         super(arg0, arg1, arg2, arg3);
         // TODO Auto-generated constructor stub
     }
     public BookStockException(String arg0, Throwable arg1) {
         super(arg0, arg1);
         // TODO Auto-generated constructor stub
     }
     public BookStockException(String arg0) {
         super(arg0);
         // TODO Auto-generated constructor stub
     }
     public BookStockException(Throwable arg0) {
         super(arg0);
         // TODO Auto-generated constructor stub
     }
 }
UserAccountException
 package com.yl.spring.tx;
 public class UserAccountException extends RuntimeException {
     /**
      *
      */
     private static final long serialVersionUID = 1L;
     public UserAccountException() {
         super();
         // TODO Auto-generated constructor stub
     }
     public UserAccountException(String arg0, Throwable arg1, boolean arg2,
             boolean arg3) {
         super(arg0, arg1, arg2, arg3);
         // TODO Auto-generated constructor stub
     }
     public UserAccountException(String arg0, Throwable arg1) {
         super(arg0, arg1);
         // TODO Auto-generated constructor stub
     }
     public UserAccountException(String arg0) {
         super(arg0);
         // TODO Auto-generated constructor stub
     }
     public UserAccountException(Throwable arg0) {
         super(arg0);
         // TODO Auto-generated constructor stub
     }
 }
Cashier
 package com.yl.spring.tx;
 import java.util.List;
 public interface Cashier {
     public void checkout(String username, List<String>isbns);
 }
CashierImpl。CashierImpl.checkout和bookShopService.purchase联合测试了事务的传播行为
package com.yl.spring.tx; import java.util.List; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; @Service("cashier")
public class CashierImpl implements Cashier {
@Autowired
private BookShopService bookShopService; @Transactional
@Override
public void checkout(String username, List<String> isbns) {
for(String isbn : isbns) {
bookShopService.purchase(username, isbn);
} } }
测试类:
package com.yl.spring.tx; import java.util.Arrays; import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringTransitionTest { private ApplicationContext ctx = null;
private BookShopDao bookShopDao = null;
private BookShopService bookShopService = null;
private Cashier cashier = null; {
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.class);
cashier = ctx.getBean(Cashier.class);
} @Test
public void testBookShopDaoFindPriceByIsbn() {
System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
} @Test
public void testBookShopDaoUpdateBookStock(){
bookShopDao.updateBookStock("1001");
} @Test
public void testBookShopDaoUpdateUserAccount(){
bookShopDao.updateUserAccount("AA", 100);
}
@Test
public void testBookShopService(){
bookShopService.purchase("AA", "1001");
} @Test
public void testTransactionPropagation(){
cashier.checkout("AA", Arrays.asList("1001", "1002"));
} }
Spring中的事务管理的更多相关文章
- Spring中的事务管理详解
		
在这里主要介绍Spring对事务管理的一些理论知识,实战方面参考上一篇博文: http://www.cnblogs.com/longshiyVip/p/5061547.html 1. 事务简介: 事务 ...
 - Spring中的事务管理(学习笔记)
		
什么是事物? 事物是指逻辑上的一组操作,这组操作要么全部成功,要么全部失败. 事物的特性: 原子性.一致性.隔离性.持久性 Spring事务管理的高级接口: PlatformTransactionMa ...
 - Spring框架学习笔记(10)——Spring中的事务管理
		
什么是事务 举例:A给B转500,两个动作,A的账户少500,B的账户多500 事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用 一.注解添加事务管理方 ...
 - Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制
		
Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制 201311.27 代码下载 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 前言 ...
 - Spring入门5.事务管理机制
		
Spring入门5.事务管理机制 20131126 代码下载 : 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 回顾之前的知识,Spring 最为核心的两个部分 ...
 - spring声明式事务管理总结
		
事务配置 首先在/WEB-INF/applicationContext.xml添加以下内容: <!-- 配置事务管理器 --> <bean id="transactionM ...
 - Spring声明式事务管理
		
一.Spring 的声明式事务管理概述 1.Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法 ...
 - spring 声明式事务管理
		
简单理解事务: 比如你去ATM机取5000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉5000元钱:然后ATM出5000元钱.这两个步骤必须是要么都执行要么都不执行.如果银行卡扣除了5000块但 ...
 - Spring声明式事务管理基于@Transactional注解
		
概述:我们已知道Spring声明式事务管理有两种常用的方式,一种是基于tx/aop命名空间的xml配置文件,另一种则是基于@Transactional 注解. 第一种方式我已在上文为大 ...
 
随机推荐
- dive into python 读笔(1)
			
chapter2 and 3: 使用Python IDE来交互式地测试表达式 编写Python程序并且从IDE运行,或者从命令行运行 导入模块及调用它们的函数 声明函数以及doc string.局部变 ...
 - Hadoop的安装与配置说明
			
安装hadoop是一件非常容易的事情,读者可以在官网上下载到最近的几个hadoop版本.网址为http://apache.etoak.com//hadoop/core/. 目前,hadoop可以运 ...
 - 1509 -- Glass Beads POJ
			
题意:求一个字符串的最小表示的开始下标 就当模板题写了 把字符串重复一遍,再建后缀自动机,贪心的选最小字典序在上面走len步 因为走出来的一定是子串,长度又是len,所以一定是原来的字符串旋转得到的, ...
 - js的原型链
			
js中的原型链是一个很重要的概念,理解了原型链,对js程序的开发有很大的好处,废话不说,先上图: javascript是基于原型的语言,所以一个对象可以另一个对象继承.不过javascript实现的时 ...
 - Unity3d + NGUI 的多分辨率适配(黑边)
			
原地址:http://www.2cto.com/kf/201310/250921.html 一.当下移动设备的主流分辨率(数据来自“腾讯分析移动设备屏幕分辨率分析报告”) 1.1 iOS设备的分辨率主 ...
 - CloudTest 事务监控:千呼万唤始出来
			
SmartBear 数据表明,如果 Amazon 的加载时间延长 1 秒,那么一年就会减少 16 亿美元的营收.用户与网站互动的过程中,如果加载时间超过3秒,57% 的用户会流失.可见,网站的加载时间 ...
 - C++11 生产者消费者
			
下面是一个生产者消费者问题,来介绍condition_variable的用法.当线程间的共享数据发生变化的时候,可以通过condition_variable来通知其他的线程.消费者wait 直到生产者 ...
 - ECSHOP报错误Deprecated: preg_replace(): The /e modifier is depr
			
http://www.ecshoptemplate.com/article-1850.html
 - Ubuntu环境下手动配置tomcat
			
配置tomcat 前提条件:JDK已配置. (配置JDK:http://www.cnblogs.com/xxx0624/p/4164744.html) 1. 下载tomcat(http://tomca ...
 - hdu 4557 非诚勿扰
			
水题…… 代码如下: #include<iostream> #include<stdio.h> #include<algorithm> #include<io ...