接下来我们重点来学习spring中的事务管理

接下来我们通过代码来实现

spring实现事物我们采用aop的方式来实现

获得连接和手动设置事物相当于@before标注的前置通知,conn.commit()相当于@AfterReturn通知,conn,close相当于@After,这样我们就可以使用spring的aop来实现事物的管理

我们采用基于xml的方式来实现

我们实现用户买书的功能,一次用户只能买一本书,买书的时候有两个动作的操作,将书的库存减去一,第二更新用户的账户信息

我们采用spring提供的jdbc操作数据库

想创建一个数据库,下面存在两个表

我们首先创建下面的几个数据库表

  1. CREATE TABLE `book` (
  2. `isbn` int(11) DEFAULT NULL,
  3. `book_name` varchar(255) DEFAULT NULL,
  4. `price` decimal(10,2) DEFAULT NULL
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  1. CREATE TABLE `account` (
  2. `username` varchar(255) DEFAULT NULL,
  3. `balance` decimal(10,0) DEFAULT NULL
  4. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  1. CREATE TABLE `book_stock` (
  2. `isbn` int(11) DEFAULT NULL,
  3. `stock` int(11) DEFAULT NULL
  4. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我们来看下面的代码

  1. package com.atguigu.spring.tx;
  2.  
  3. public interface BookShopDao {
  4.  
  5. //根据书号获取书的单价
  6. public int findBookPriceByIsbn(String isbn);
  7.  
  8. //更新数的库存. 使书号对应的库存 - 1
  9. public void updateBookStock(String isbn);
  10.  
  11. //更新用户的账户余额: 使 username 的 balance - price
  12. public void updateUserAccount(String username, int price);
  13. }
  1. package com.atguigu.spring.tx;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.jdbc.core.JdbcTemplate;
  5. import org.springframework.stereotype.Repository;
  6.  
  7. @Repository("bookShopDao")
  8. public class BookShopDaoImpl implements BookShopDao {
  9.  
  10. @Autowired
  11. private JdbcTemplate jdbcTemplate;
  12.  
  13. @Override
  14. public int findBookPriceByIsbn(String isbn) {
  15. String sql = "SELECT price FROM book WHERE isbn = ?";
  16. return jdbcTemplate.queryForObject(sql, Integer.class, isbn);
  17. }
  18.  
  19. @Override
  20. public void updateBookStock(String isbn) {
  21. //检查书的库存是否足够, 若不够, 则抛出异常
  22. String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?";
  23. int stock = jdbcTemplate.queryForObject(sql2, Integer.class, isbn);
  24. if(stock == 0){
  25. throw new BookStockException("库存不足!");
  26. }
  27.  
  28. String sql = "UPDATE book_stock SET stock = stock -1 WHERE isbn = ?";
  29. jdbcTemplate.update(sql, isbn);
  30. }
  31.  
  32. @Override
  33. public void updateUserAccount(String username, int price) {
  34. //验证余额是否足够, 若不足, 则抛出异常
  35. String sql2 = "SELECT balance FROM account WHERE username = ?";
  36. int balance = jdbcTemplate.queryForObject(sql2, Integer.class, username);
  37. if(balance < price){
  38. throw new UserAccountException("余额不足!");
  39. }
  40.  
  41. String sql = "UPDATE account SET balance = balance - ? WHERE username = ?";
  42. jdbcTemplate.update(sql, price, username);
  43. }
  44.  
  45. }

业务类的操作

  1. package com.atguigu.spring.tx;
  2.  
  3. public interface BookShopService {
  4.  
  5. public void purchase(String username, String isbn);
  6.  
  7. }
  1. package com.atguigu.spring.tx;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.transaction.annotation.Isolation;
  6. import org.springframework.transaction.annotation.Propagation;
  7. import org.springframework.transaction.annotation.Transactional;
  8.  
  9. @Service("bookShopService")
  10. public class BookShopServiceImpl implements BookShopService {
  11.  
  12. @Autowired
  13. private BookShopDao bookShopDao;
  14.  
  15. @Override
  16. public void purchase(String username, String isbn) {
  17.  
  18. try {
  19. Thread.sleep(5000);
  20. } catch (InterruptedException e) {}
  21.  
  22. //1. 获取书的单价
  23. int price = bookShopDao.findBookPriceByIsbn(isbn);
  24.  
  25. //2. 更新数的库存
  26. bookShopDao.updateBookStock(isbn);
  27.  
  28. //3. 更新用户余额
  29. bookShopDao.updateUserAccount(username, price);
  30. }
  31.  
  32. }
  1. package com.atguigu.spring.tx;
  2.  
  3. public class BookStockException extends RuntimeException{
  4.  
  5. /**
  6. *
  7. */
  8. private static final long serialVersionUID = 1L;
  9.  
  10. public BookStockException() {
  11. super();
  12. // TODO Auto-generated constructor stub
  13. }
  14.  
  15. public BookStockException(String message, Throwable cause,
  16. boolean enableSuppression, boolean writableStackTrace) {
  17. super(message, cause, enableSuppression, writableStackTrace);
  18. // TODO Auto-generated constructor stub
  19. }
  20.  
  21. public BookStockException(String message, Throwable cause) {
  22. super(message, cause);
  23. // TODO Auto-generated constructor stub
  24. }
  25.  
  26. public BookStockException(String message) {
  27. super(message);
  28. // TODO Auto-generated constructor stub
  29. }
  30.  
  31. public BookStockException(Throwable cause) {
  32. super(cause);
  33. // TODO Auto-generated constructor stub
  34. }
  35.  
  36. }

测试代码如下

  1. package com.atguigu.spring.tx;
  2.  
  3. import static org.junit.Assert.*;
  4.  
  5. import java.util.Arrays;
  6.  
  7. import org.junit.Test;
  8. import org.springframework.context.ApplicationContext;
  9. import org.springframework.context.support.ClassPathXmlApplicationContext;
  10.  
  11. public class SpringTransactionTest {
  12.  
  13. private ApplicationContext ctx = null;
  14. private BookShopDao bookShopDao = null;
  15. private BookShopService bookShopService = null;
  16. private Cashier cashier = null;
  17.  
  18. {
  19. ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
  20. bookShopDao = ctx.getBean(BookShopDao.class);
  21. bookShopService = ctx.getBean(BookShopService.class);
  22. cashier = ctx.getBean(Cashier.class);
  23. }
  24.  
  25. @Test
  26. public void testBookShopService(){
  27. bookShopService.purchase("AA", "1001");
  28. }
  29.  
  30. }

一个用户购买一本书,就需要上面的三个步骤

第一步:

  1. //1. 获取书的单价
  1. //2. 更新数的库存
  1. //3. 更新用户余额
    接下来我们重点讲解下事物
  2.  
  3. 上面的操作过程如果不加事务的话,会出现异常,例如现在用户AA的账户上只有60块钱,购买一本java书籍需要100元,购买的时候将java库存表的时候已经将书的库存减去一,但是在更加账户
  1. 余额的时候去发生异常抛出
  1. 余额不足的异常,这个就会导致信息不同步,这个时候就需要事务来进行管理
  2.  
  3. 第一步需要在xml配置文件中添加一个事务管理器,这里是采用jdbc的事务管理器,事务管理器是对数据源进行事务管理,需要注入数据源
    第二:需要启动事务管理
    第三:在service中的操作方法中添加事务注解
    applicationContext.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
  8. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
  9.  
  10. <context:component-scan base-package="com.atguigu.spring"></context:component-scan>
  11.  
  12. <!-- 导入资源文件 -->
  13. <context:property-placeholder location="classpath:db.properties"/>
  14.  
  15. <!-- 配置 C3P0 数据源 -->
  16. <bean id="dataSource"
  17. class="com.mchange.v2.c3p0.ComboPooledDataSource">
  18. <property name="user" value="${jdbc.user}"></property>
  19. <property name="password" value="${jdbc.password}"></property>
  20. <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
  21. <property name="driverClass" value="${jdbc.driverClass}"></property>
  22.  
  23. <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
  24. <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
  25. </bean>
  26.  
  27. <!-- 配置 Spirng 的 JdbcTemplate -->
  28. <bean id="jdbcTemplate"
  29. class="org.springframework.jdbc.core.JdbcTemplate">
  30. <property name="dataSource" ref="dataSource"></property>
  31. </bean>
  32.  
  33. <!-- 配置 NamedParameterJdbcTemplate, 该对象可以使用具名参数, 其没有无参数的构造器, 所以必须为其构造器指定参数 -->
  34. <bean id="namedParameterJdbcTemplate"
  35. class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
  36. <constructor-arg ref="dataSource"></constructor-arg>
  37. </bean>
  38.  
  39. <!-- 配置事务管理器 -->
  40. <bean id="transactionManager"
  41. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  42. <property name="dataSource" ref="dataSource"></property>
  43. </bean>
  44.  
  45. <!-- 启用事务注解 -->
  46. <tx:annotation-driven transaction-manager="transactionManager"/>
  47.  
  48. </beans>
  1.  
  1.  

在service的业务方法分钟添加注解

  1. package com.atguigu.spring.tx;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.transaction.annotation.Isolation;
  6. import org.springframework.transaction.annotation.Propagation;
  7. import org.springframework.transaction.annotation.Transactional;
  8.  
  9. @Service("bookShopService")
  10. public class BookShopServiceImpl implements BookShopService {
  11.  
  12. @Autowired
  13. private BookShopDao bookShopDao;
  14.  
  15. @Transactional
  16. @Override
  17. public void purchase(String username, String isbn) {
  18.  
  19. try {
  20. Thread.sleep(5000);
  21. } catch (InterruptedException e) {}
  22.  
  23. //1. 获取书的单价
  24. int price = bookShopDao.findBookPriceByIsbn(isbn);
  25.  
  26. //2. 更新数的库存
  27. bookShopDao.updateBookStock(isbn);
  28.  
  29. //3. 更新用户余额
  30. bookShopDao.updateUserAccount(username, price);
  31. }
  32.  
  33. }

这样我们就达到了事务管理的操作

上面一个最简单的事务操作已经完成了,接下面我们进一步研究事务的隔离级别、事务的过期时间、事务的传播行为等进行研究

什么叫事务传播行为?听起来挺高端的,其实很简单。 
即然是传播,那么至少有两个东西,才可以发生传播。单体不存在传播这个行为。

事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。 
例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

Spring定义了七种传播行为:

接下来我们通过代码来实现,我们再定义一个接口,让一个人可以购买多本书

  1. package com.atguigu.spring.tx;
  2.  
  3. import java.util.List;
  4.  
  5. public interface Cashier {
  6.  
  7. public void checkout(String username, List<String> isbns);
  8.  
  9. }
  1. package com.atguigu.spring.tx;
  2.  
  3. import java.util.List;
  4.  
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Service;
  7. import org.springframework.transaction.annotation.Transactional;
  8.  
  9. @Service("cashier")
  10. public class CashierImpl implements Cashier {
  11.  
  12. @Autowired
  13. private BookShopService bookShopService;
  14.  
  15. @Transactional
  16. @Override
  17. public void checkout(String username, List<String> isbns) {
  18. for(String isbn: isbns){
  19. bookShopService.purchase(username, isbn);
  20. }
  21. }
  22.  
  23. }

一个人购买多本书的方法checkout使用了 @Transactional标注,说明该方法是一个事务方法,然后该方法调用了bookShopService.purchase,这个方法也是事务方法,一个事务方法调用了另外一个事务方法

我们来看下测试方法

  1. package com.atguigu.spring.tx;
  2.  
  3. import static org.junit.Assert.*;
  4.  
  5. import java.util.Arrays;
  6.  
  7. import org.junit.Test;
  8. import org.springframework.context.ApplicationContext;
  9. import org.springframework.context.support.ClassPathXmlApplicationContext;
  10.  
  11. public class SpringTransactionTest {
  12.  
  13. private ApplicationContext ctx = null;
  14. private BookShopDao bookShopDao = null;
  15. private BookShopService bookShopService = null;
  16. private Cashier cashier = null;
  17.  
  18. {
  19. ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
  20. bookShopDao = ctx.getBean(BookShopDao.class);
  21. bookShopService = ctx.getBean(BookShopService.class);
  22. cashier = ctx.getBean(Cashier.class);
  23. }
  24.  
  25. @Test
  26. public void testTransactionlPropagation(){
  27. cashier.checkout("AA", Arrays.asList("1001", "1002"));
  28. }
  29.  
  30. }

我们来看下下面的业务操作过程

如果采用required特性,在购买1001这本书的时候购买成功了,但是在购买1002的时候抛出了异常,这个时候因为采用的是required特性,这个时候购买书的操作都使用checkout这个方法的事务中,抛出异常

会对事务的全部操作进行回滚,但是购买的1001成功的操作也会回滚,导致一本书都不会购买成功

接下来第二种事务

如果采用required-new特性,在购买1001这本书的时候购买成功了,但是在购买1002的时候抛出了异常,这个时候因为采用的是required-new特性,这个时候在purches中会开启一个新的事务

只会对异常操作的事务进行回滚,购买的1001成功的操作不会回滚,后面1001会购买成功,不会回滚,只会回滚购买1002的操作

制定required-new特性需要对serivice进行配置

  1. package com.atguigu.spring.tx;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.transaction.annotation.Isolation;
  6. import org.springframework.transaction.annotation.Propagation;
  7. import org.springframework.transaction.annotation.Transactional;
  8.  
  9. @Service("bookShopService")
  10. public class BookShopServiceImpl implements BookShopService {
  11.  
  12. @Autowired
  13. private BookShopDao bookShopDao;
  14.  
  15. @Transactional(propagation = Propagation.REQUIRES_NEW)
  16. @Override
  17. public void purchase(String username, String isbn) {
  18.  
  19. try {
  20. Thread.sleep(5000);
  21. } catch (InterruptedException e) {}
  22.  
  23. //1. 获取书的单价
  24. int price = bookShopDao.findBookPriceByIsbn(isbn);
  25.  
  26. //2. 更新数的库存
  27. bookShopDao.updateBookStock(isbn);
  28.  
  29. //3. 更新用户余额
  30. bookShopDao.updateUserAccount(username, price);
  31. }
  32.  
  33. }

对于事务的操作一般掌握上面的两种就可以了

接下来我们学校事务的隔离级别

3.Spring事务隔离级别:spring有五大隔离级别,其在TransactionDefinition接口中定义。看源码可知,其默isolation_default(底层数据库默认级别),其他四个隔离级别跟数据库隔离级别一致。
ISOLATION_DEFAULT:用底层数据库的默认隔离级别,数据库管理员设置什么就是什么
ISOLATION_READ_UNCOMMITTED(未提交读):最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)
ISOLATION_READ_COMMITTED(提交读):一个事务提交后才能被其他事务读取到(该隔离级别禁止其他事务读取到未提交事务的数据、所以还是会造成幻读、不可重复读)、sql server默认级别
ISOLATION_REPEATABLE_READ(可重复读):可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(该隔离基本可防止脏读,不可重复读(重点在修改),但会出现幻读(重点在增加与删除))(MySql默认级别,更改可通过set transaction isolation level 级别)
ISOLATION_SERIALIZABLE(序列化):代价最高最可靠的隔离级别(该隔离级别能防止脏读、不可重复读、幻读)
丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的;
幻读:同样的事务操作过程中,不同时间段多次(不同事务)读取同一数据,读取到的内容不一致(一般是行数变多或变少)。
脏读:一个事务读取到另外一个未提及事务的内容,即为脏读。
不可重复读:同一事务中,多次读取内容不一致(一般行数不变,而内容变了)。
幻读与不可重复读的区别:幻读的重点在于插入与删除,即第二次查询会发现比第一次查询数据变少或者变多了,以至于给人一种幻象一样,而不可重复读重点在于修改,即第二次查询会发现查询结果比第一次查询结果不一致,即第一次结果已经不可重现了。

数据库隔离级别越高,执行代价越高,并发执行能力越差,因此在实际项目开发使用时要综合考虑,为了考虑并发性能一般使用提交读隔离级别,它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁或乐观锁来解决这些问题。

  1. package com.atguigu.spring.tx;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.transaction.annotation.Isolation;
  6. import org.springframework.transaction.annotation.Propagation;
  7. import org.springframework.transaction.annotation.Transactional;
  8.  
  9. @Service("bookShopService")
  10. public class BookShopServiceImpl implements BookShopService {
  11.  
  12. @Autowired
  13. private BookShopDao bookShopDao;
  14.  
  15. //添加事务注解
  16. //1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时
  17. //如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
  18. //REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起.
  19. //2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED
  20. //3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的
  21. //属性进行设置. 通常情况下去默认值即可.
  22. //4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据,
  23. //这样可以帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true
  24. //5.使用 timeout 指定强制回滚之前事务可以占用的时间.
  25. // @Transactional(propagation=Propagation.REQUIRES_NEW,
  26. // isolation=Isolation.READ_COMMITTED,
  27. // noRollbackFor={UserAccountException.class})
  28. @Transactional(propagation=Propagation.REQUIRES_NEW,
  29. isolation=Isolation.READ_COMMITTED,
  30. readOnly=false,
  31. timeout=3)
  32. @Override
  33. public void purchase(String username, String isbn) {
  34.  
  35. try {
  36. Thread.sleep(5000);
  37. } catch (InterruptedException e) {}
  38.  
  39. //1. 获取书的单价
  40. int price = bookShopDao.findBookPriceByIsbn(isbn);
  41.  
  42. //2. 更新数的库存
  43. bookShopDao.updateBookStock(isbn);
  44.  
  45. //3. 更新用户余额
  46. bookShopDao.updateUserAccount(username, price);
  47. }
  48.  
  49. }

上面我们讲解了通过注解的方式来管理事务,接下面我们通过xml的方式来进行事务的管理

工程的代码如下:

  1. package com.atguigu.spring.tx.xml;
  2.  
  3. import org.springframework.jdbc.core.JdbcTemplate;
  4.  
  5. public class BookShopDaoImpl implements BookShopDao {
  6.  
  7. private JdbcTemplate jdbcTemplate;
  8.  
  9. public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  10. this.jdbcTemplate = jdbcTemplate;
  11. }
  12.  
  13. @Override
  14. public int findBookPriceByIsbn(String isbn) {
  15. String sql = "SELECT price FROM book WHERE isbn = ?";
  16. return jdbcTemplate.queryForObject(sql, Integer.class, isbn);
  17. }
  18.  
  19. @Override
  20. public void updateBookStock(String isbn) {
  21. //检查书的库存是否足够, 若不够, 则抛出异常
  22. String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?";
  23. int stock = jdbcTemplate.queryForObject(sql2, Integer.class, isbn);
  24. if(stock == 0){
  25. throw new BookStockException("库存不足!");
  26. }
  27.  
  28. String sql = "UPDATE book_stock SET stock = stock -1 WHERE isbn = ?";
  29. jdbcTemplate.update(sql, isbn);
  30. }
  31.  
  32. @Override
  33. public void updateUserAccount(String username, int price) {
  34. //验证余额是否足够, 若不足, 则抛出异常
  35. String sql2 = "SELECT balance FROM account WHERE username = ?";
  36. int balance = jdbcTemplate.queryForObject(sql2, Integer.class, username);
  37. if(balance < price){
  38. throw new UserAccountException("余额不足!");
  39. }
  40.  
  41. String sql = "UPDATE account SET balance = balance - ? WHERE username = ?";
  42. jdbcTemplate.update(sql, price, username);
  43. }
  44.  
  45. }
  1. package com.atguigu.spring.tx.xml;
  2.  
  3. public interface BookShopDao {
  4.  
  5. //根据书号获取书的单价
  6. public int findBookPriceByIsbn(String isbn);
  7.  
  8. //更新数的库存. 使书号对应的库存 - 1
  9. public void updateBookStock(String isbn);
  10.  
  11. //更新用户的账户余额: 使 username 的 balance - price
  12. public void updateUserAccount(String username, int price);
  13. }
  1. package com.atguigu.spring.tx.xml.service;
  2.  
  3. public interface BookShopService {
  4.  
  5. public void purchase(String username, String isbn);
  6.  
  7. }
  1. package com.atguigu.spring.tx.xml.service;
  2.  
  3. import java.util.List;
  4.  
  5. public interface Cashier {
  6.  
  7. public void checkout(String username, List<String> isbns);
  8.  
  9. }
  1. package com.atguigu.spring.tx.xml.service.impl;
  2.  
  3. import com.atguigu.spring.tx.xml.BookShopDao;
  4. import com.atguigu.spring.tx.xml.service.BookShopService;
  5.  
  6. public class BookShopServiceImpl implements BookShopService {
  7.  
  8. private BookShopDao bookShopDao;
  9.  
  10. public void setBookShopDao(BookShopDao bookShopDao) {
  11. this.bookShopDao = bookShopDao;
  12. }
  13.  
  14. @Override
  15. public void purchase(String username, String isbn) {
  16.  
  17. try {
  18. Thread.sleep(1000);
  19. } catch (InterruptedException e) {}
  20.  
  21. //1. 获取书的单价
  22. int price = bookShopDao.findBookPriceByIsbn(isbn);
  23.  
  24. //2. 更新数的库存
  25. bookShopDao.updateBookStock(isbn);
  26.  
  27. //3. 更新用户余额
  28. bookShopDao.updateUserAccount(username, price);
  29. }
  30.  
  31. }
  1. package com.atguigu.spring.tx.xml.service.impl;
  2.  
  3. import java.util.List;
  4.  
  5. import com.atguigu.spring.tx.xml.service.BookShopService;
  6. import com.atguigu.spring.tx.xml.service.Cashier;
  7.  
  8. public class CashierImpl implements Cashier {
  9.  
  10. private BookShopService bookShopService;
  11.  
  12. public void setBookShopService(BookShopService bookShopService) {
  13. this.bookShopService = bookShopService;
  14. }
  15.  
  16. @Override
  17. public void checkout(String username, List<String> isbns) {
  18. for(String isbn: isbns){
  19. bookShopService.purchase(username, isbn);
  20. }
  21. }
  22.  
  23. }

我们来看看最为关键的代码

applicationContext-tx-xml.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xmlns:aop="http://www.springframework.org/schema/aop"
  7. xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
  8. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  9. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
  10. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
  11.  
  12. <context:component-scan base-package="com.atguigu.spring"></context:component-scan>
  13.  
  14. <!-- 导入资源文件 -->
  15. <context:property-placeholder location="classpath:db.properties"/>
  16.  
  17. <!-- 配置 C3P0 数据源 -->
  18. <bean id="dataSource"
  19. class="com.mchange.v2.c3p0.ComboPooledDataSource">
  20. <property name="user" value="${jdbc.user}"></property>
  21. <property name="password" value="${jdbc.password}"></property>
  22. <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
  23. <property name="driverClass" value="${jdbc.driverClass}"></property>
  24.  
  25. <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
  26. <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
  27. </bean>
  28.  
  29. <!-- 配置 Spirng 的 JdbcTemplate -->
  30. <bean id="jdbcTemplate"
  31. class="org.springframework.jdbc.core.JdbcTemplate">
  32. <property name="dataSource" ref="dataSource"></property>
  33. </bean>
  34.  
  35. <!-- 配置 bean -->
  36. <bean id="bookShopDao" class="com.atguigu.spring.tx.xml.BookShopDaoImpl">
  37. <property name="jdbcTemplate" ref="jdbcTemplate"></property>
  38. </bean>
  39.  
  40. <bean id="bookShopService" class="com.atguigu.spring.tx.xml.service.impl.BookShopServiceImpl">
  41. <property name="bookShopDao" ref="bookShopDao"></property>
  42. </bean>
  43.  
  44. <bean id="cashier" class="com.atguigu.spring.tx.xml.service.impl.CashierImpl">
  45. <property name="bookShopService" ref="bookShopService"></property>
  46. </bean>
  47.  
  48. <!-- 1. 配置事务管理器 -->
  49. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  50. <property name="dataSource" ref="dataSource"></property>
  51. </bean>
  52.  
  53. <!-- 2. 配置事务属性 -->
  54. <tx:advice id="txAdvice" transaction-manager="transactionManager">
  55. <tx:attributes>
  56. <!-- 根据方法名指定事务的属性 -->
  57. <tx:method name="purchase" propagation="REQUIRES_NEW"/>
  58. <tx:method name="get*" read-only="true"/>
  59. <tx:method name="find*" read-only="true"/>
  60. <tx:method name="*"/>
  61. </tx:attributes>
  62. </tx:advice>
  63.  
  64. <!-- 3. 配置事务切入点, 以及把事务切入点和事务属性关联起来 -->
  65. <aop:config>
  66. <aop:pointcut expression="execution(* com.atguigu.spring.tx.xml.service.*.*(..))"
  67. id="txPointCut"/>
  68. <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
  69. </aop:config>
  70.  
  71. </beans>

注意点:jdbc对应的事务管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager

mybatis这里就有进行修改

上面有几个点需要特别注意

1、事务传播特性的时候:两个事务是在不同的service中,CashierImpl中checkout方法中添加了事务

然后在BookShopServiceImpl类的purchase方法中添加了事务,checkout方法调用了purchase方法,不同service中的事务相互调用

2、如果是同一个service中的两个事务方法会存在这样的问题了?后面会分析,在开发的时候要避免一个service中两个事务方法相互调用,因为会存在很多问题

相当的经典

上面对于的代码在网上下载:

尚硅谷_佟刚_Spring_完整代码

尚硅谷spring 事物管理的更多相关文章

  1. MyBatis6:MyBatis集成Spring事物管理(下篇)

    前言 前一篇文章<MyBatis5:MyBatis集成Spring事物管理(上篇)>复习了MyBatis的基本使用以及使用Spring管理MyBatis的事物的做法,本文的目的是在这个的基 ...

  2. Spring事物管理--相关要点及配置事物管理器

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

  3. spring 事物管理没起到作用

    今天在做项目的时候发现配置的spring 事物管理没起到作用.可是配置又是依据官网配置的,不可能会错.最后发现使mysql的问题 普通情况下,mysql会默认提供多种存储引擎,你能够通过以下的查看: ...

  4. java陷阱之spring事物管理导致锁无效

    模拟锁情况无效 1.创建一个表 SET NAMES utf8mb4; ; DROP TABLE IF EXISTS `demo`; CREATE TABLE `demo` ( `id` ) NOT N ...

  5. Spring事物管理简介 (转)

    一.事物1.什么是事物 事物指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败 2.事物的特性 原子性:事物是一个不可分割的工作单位,事物中的操作要么都发生,要么都不发生 一致性:事物前后数据 ...

  6. 20191003 尚硅谷Spring Cloud教学视频

    视频信息 视频日期:2018-4-19 讲师:尚硅谷周阳 Spring Cloud版本:Dalston.RELEASE 当前版本:Greenwich SR3 微服务.微服务架构.Spring Clou ...

  7. 集成Spring事物管理

    什么是事物 事物是访问数据库的一个操作序列,数据库应用系统通过事物集来完成对数据库的存取.事物的正确执行使得数据库从一种状态转换为另一种状态. 事物必须服从ISO/IEC所制定的ACID原则.ACID ...

  8. Spring事物管理机制简单学习

    首先spring并不直接管理事物,而是提供了多种事物管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现. Spring管理事物的接口是org.s ...

  9. 【JAVA】Spring 事物管理

            在Spring事务管理中通过TransactionProxyFactoryBean配置事务信息,此类通过3个重要接口完成事务的配置及相关操作,分别是PlatformTransactio ...

随机推荐

  1. Linux显示行号设置

    linux显示行号设置 第一步,打开vim vi ~/.vimrc 第二步,在该文件中加入一行,命令如下: set nu # 显示行号 set nonu # 不显示行号 微信公众号:喵哥解说 公众号介 ...

  2. super调用父类的属性方法

    super:可以用来修饰属性  方法   构造器 当子类与父类中有同名的属性时,可以通过   super.此属性  显式的调用父类声明的属性 若想调用子类的同名的属性“this.此属性” 2.当子类重 ...

  3. group by和having注意事项

    执行和编写顺序:join->where->group by->having 其中where与having的区别: where用于在group by分组之前过滤掉某些行, group ...

  4. 电子邮件协议及GO发送QQ邮件

    目录 一.电子邮件的工作机制 1.1 SMTP 1.2 POP3 1.3 IMAP 二.邮件地址 三.MIME信息 四.使用golang发送qq邮件 一.电子邮件的工作机制 提供电子邮件服务的协议叫做 ...

  5. ES6-for...of与for...in

    1.includes 数组是否包含某个东西 <!DOCTYPE html> <html lang="en"> <head> <meta c ...

  6. Java实现 LeetCode 832 翻转图像(位运算)

    832. 翻转图像 给定一个二进制矩阵 A,我们想先水平翻转图像,然后反转图像并返回结果. 水平翻转图片就是将图片的每一行都进行翻转,即逆序.例如,水平翻转 [1, 1, 0] 的结果是 [0, 1, ...

  7. (Java实现) 组合的输出

    问题 B: [递归入门]组合的输出 时间限制: 1 Sec 内存限制: 128 MB 题目描述 排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r < = n),我们 ...

  8. Java实现 蓝桥杯 基础练习 特殊的数字

    基础练习 特殊的数字 时间限制:1.0s 内存限制:512.0MB 提交此题 锦囊1 锦囊2 问题描述 153是一个非常特殊的数,它等于它的每位数字的立方和,即153=111+555+333.编程求所 ...

  9. C++实现网络寻路

    标题:网络寻路 X 国的一个网络使用若干条线路连接若干个节点.节点间的通信是双向的.某重要数据包,为了安全起见,必须恰好被转发两次到达目的地.该包可能在任意一个节点产生,我们需要知道该网络中一共有多少 ...

  10. java实现第六届蓝桥杯星系炸弹

    星系炸弹 题目描述 在X星系的广袤空间中漂浮着许多X星人造"炸弹",用来作为宇宙中的路标. 每个炸弹都可以设定多少天之后爆炸. 比如:阿尔法炸弹2015年1月1日放置,定时为15天 ...