事务简介:
  事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性
  事务就是一系列的动作,它们被当作一个单独的工作单元。这些动作要么全部完成,要么全部不起作用
  事务的四个关键属性(ACID)
    原子性(atomicity):事务室一个原子操作,有一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用
    一致性(consistency):一旦所有事务动作完成,事务就被提交。数据和资源就处于一种满足业务规则的一致性状态中
    隔离性(isolation):可能有许多事务会同时处理相同的数据,因此每个事物都应该与其他事务隔离开来,防止数据损坏
    持久性(durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响。通常情况下,事务的结果被写到持久化存储器中

Spring中的事务管理
  作为企业级应用程序框架,Spring在不同的事务管理API之上定义了一个抽象层。而应用程序开发人员不必了解底层的事务管理API,就可以使用Spring的事务管理机制。

Spring既支持编程式事务管理,也支持声明式的事务管理
  编程式事务管理:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚,在编程式事务中,必须在每个业务操作中包含额外的事务管理代码
  声明式事务管理:大多数情况下比编程式事务管理更好用。它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。事务管理作为一种横切关注点,可以通过AOP方法模块化。Spring通过Spring AOP框架支持声明式事务管理。

Spring事务的传播属性:

  当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
  事务的传播行为可以由传播属性指定。Spring定义了7种传播行为:

Spring支持的事务传播行为
传播行为 含义
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中的事务管理的更多相关文章

  1. Spring中的事务管理详解

    在这里主要介绍Spring对事务管理的一些理论知识,实战方面参考上一篇博文: http://www.cnblogs.com/longshiyVip/p/5061547.html 1. 事务简介: 事务 ...

  2. Spring中的事务管理(学习笔记)

    什么是事物? 事物是指逻辑上的一组操作,这组操作要么全部成功,要么全部失败. 事物的特性: 原子性.一致性.隔离性.持久性 Spring事务管理的高级接口: PlatformTransactionMa ...

  3. Spring框架学习笔记(10)——Spring中的事务管理

    什么是事务 举例:A给B转500,两个动作,A的账户少500,B的账户多500 事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用 一.注解添加事务管理方 ...

  4. Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制

    Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制 201311.27 代码下载 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 前言 ...

  5. Spring入门5.事务管理机制

    Spring入门5.事务管理机制 20131126 代码下载 : 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 回顾之前的知识,Spring 最为核心的两个部分 ...

  6. spring声明式事务管理总结

    事务配置 首先在/WEB-INF/applicationContext.xml添加以下内容: <!-- 配置事务管理器 --> <bean id="transactionM ...

  7. Spring声明式事务管理

    一.Spring 的声明式事务管理概述 1.Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法 ...

  8. spring 声明式事务管理

    简单理解事务: 比如你去ATM机取5000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉5000元钱:然后ATM出5000元钱.这两个步骤必须是要么都执行要么都不执行.如果银行卡扣除了5000块但 ...

  9. Spring声明式事务管理基于@Transactional注解

    概述:我们已知道Spring声明式事务管理有两种常用的方式,一种是基于tx/aop命名空间的xml配置文件,另一种则是基于@Transactional 注解.         第一种方式我已在上文为大 ...

随机推荐

  1. couldnt resolve host mirrorlist.centos

     解决centos 6.3 yum安装软件时找不到镜像问题 [root@nagios-server ~]# yum update –y Loaded plugins: fastestmirror Lo ...

  2. 读书笔记 (二) ———Fundamentals of Multiagent Systems with NetLogo Examples by Prof. Jose M Vidal

    chapter 2 分布式约束1 分布式约束满足 1.1 过滤算法 1.2 基于归结的调和算法 consistency 1.3 异步回溯 1.4 异步弱承诺? 1.5 分布式突破?2 分布式受限优化 ...

  3. 莫名其妙的主机名 VM_32_234_centos

    在腾讯服务器上部署的时候出现的莫名其妙的错误, 我在配置文件上都写 master, node1, 但是运行mapredure的时候的时候老是出现 Job job_1409546978133_0001 ...

  4. 1.2 XmlBeanFactory的实例化

    源代码分析,是一件既痛苦又快乐的事情,看别人写的代码是通过的,但当你能够看明白的时候,相信快乐也会随之而来,为了减少痛苦,更快的带来快乐, 本文以spring框架的XmlBeanFactory为入手点 ...

  5. Binary search for the first element greater than target

    We all know how to search through an array for an element whose value equals the target value, but h ...

  6. hdu 3952

    因为一个小错 不过  好不爽........ #include <iostream> #include <fstream> using namespace std; struc ...

  7. [笨木头FireFly01]入门篇1·最简单的服务端和客户端连接

    原地址:http://www.9miao.com/question-15-53938.html 最近一直在写游戏,几乎没有来写教程了,打算放慢一下脚步,学学新东西.那为嘛我要学FireFly呢? 之前 ...

  8. DLL搜索路径和DLL劫持

    DLL搜索路径和DLL劫持 环境:XP SP3 VS2005 作者:magictong 为什么要把DLL搜索路径(DLL ORDER)和DLL劫持(DLL Hajack)拿到一起讲呢?呵呵,其实没啥深 ...

  9. HDU 1429 胜利大逃亡(续)(三维BFS)

    题目链接 题意 : 中文题不详述. 思路 : 这个题和1885差不多一样的,所以我直接改了改那个代码就交上了,链接 #include <stdio.h> #include <stri ...

  10. [itint5]单词游戏

    http://www.itint5.com/oj/#36 此题在数据大些,而且全是A的情况下会超时(因为要匹配到很后面才false).通过利用数组本身作为visited标示,而且使用string引用, ...