尚硅谷spring 事物管理
接下来我们重点来学习spring中的事务管理
接下来我们通过代码来实现
spring实现事物我们采用aop的方式来实现
获得连接和手动设置事物相当于@before标注的前置通知,conn.commit()相当于@AfterReturn通知,conn,close相当于@After,这样我们就可以使用spring的aop来实现事物的管理
我们采用基于xml的方式来实现
我们实现用户买书的功能,一次用户只能买一本书,买书的时候有两个动作的操作,将书的库存减去一,第二更新用户的账户信息
我们采用spring提供的jdbc操作数据库
想创建一个数据库,下面存在两个表
我们首先创建下面的几个数据库表
- CREATE TABLE `book` (
- `isbn` int(11) DEFAULT NULL,
- `book_name` varchar(255) DEFAULT NULL,
- `price` decimal(10,2) DEFAULT NULL
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- CREATE TABLE `account` (
- `username` varchar(255) DEFAULT NULL,
- `balance` decimal(10,0) DEFAULT NULL
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- CREATE TABLE `book_stock` (
- `isbn` int(11) DEFAULT NULL,
- `stock` int(11) DEFAULT NULL
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
我们来看下面的代码
- package com.atguigu.spring.tx;
- public interface BookShopDao {
- //根据书号获取书的单价
- public int findBookPriceByIsbn(String isbn);
- //更新数的库存. 使书号对应的库存 - 1
- public void updateBookStock(String isbn);
- //更新用户的账户余额: 使 username 的 balance - price
- public void updateUserAccount(String username, int price);
- }
- package com.atguigu.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);
- }
- }
业务类的操作
- package com.atguigu.spring.tx;
- public interface BookShopService {
- public void purchase(String username, String isbn);
- }
- package com.atguigu.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;
- @Override
- public void purchase(String username, String isbn) {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {}
- //1. 获取书的单价
- int price = bookShopDao.findBookPriceByIsbn(isbn);
- //2. 更新数的库存
- bookShopDao.updateBookStock(isbn);
- //3. 更新用户余额
- bookShopDao.updateUserAccount(username, price);
- }
- }
- package com.atguigu.spring.tx;
- public class BookStockException extends RuntimeException{
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- public BookStockException() {
- super();
- // TODO Auto-generated constructor stub
- }
- public BookStockException(String message, Throwable cause,
- boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- // TODO Auto-generated constructor stub
- }
- public BookStockException(String message, Throwable cause) {
- super(message, cause);
- // TODO Auto-generated constructor stub
- }
- public BookStockException(String message) {
- super(message);
- // TODO Auto-generated constructor stub
- }
- public BookStockException(Throwable cause) {
- super(cause);
- // TODO Auto-generated constructor stub
- }
- }
测试代码如下
- package com.atguigu.spring.tx;
- import static org.junit.Assert.*;
- import java.util.Arrays;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class SpringTransactionTest {
- 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 testBookShopService(){
- bookShopService.purchase("AA", "1001");
- }
- }
一个用户购买一本书,就需要上面的三个步骤
第一步:
- //1. 获取书的单价
- //2. 更新数的库存
- //3. 更新用户余额
接下来我们重点讲解下事物- 上面的操作过程如果不加事务的话,会出现异常,例如现在用户AA的账户上只有60块钱,购买一本java书籍需要100元,购买的时候将java库存表的时候已经将书的库存减去一,但是在更加账户
- 余额的时候去发生异常抛出
- 余额不足的异常,这个就会导致信息不同步,这个时候就需要事务来进行管理
- 第一步需要在xml配置文件中添加一个事务管理器,这里是采用jdbc的事务管理器,事务管理器是对数据源进行事务管理,需要注入数据源
第二:需要启动事务管理
第三:在service中的操作方法中添加事务注解
applicationContext.xml
- <?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: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/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
- <context:component-scan base-package="com.atguigu.spring"></context:component-scan>
- <!-- 导入资源文件 -->
- <context:property-placeholder location="classpath:db.properties"/>
- <!-- 配置 C3P0 数据源 -->
- <bean id="dataSource"
- class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="user" value="${jdbc.user}"></property>
- <property name="password" value="${jdbc.password}"></property>
- <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
- <property name="driverClass" value="${jdbc.driverClass}"></property>
- <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
- <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
- </bean>
- <!-- 配置 Spirng 的 JdbcTemplate -->
- <bean id="jdbcTemplate"
- class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="dataSource"></property>
- </bean>
- <!-- 配置 NamedParameterJdbcTemplate, 该对象可以使用具名参数, 其没有无参数的构造器, 所以必须为其构造器指定参数 -->
- <bean id="namedParameterJdbcTemplate"
- class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
- <constructor-arg ref="dataSource"></constructor-arg>
- </bean>
- <!-- 配置事务管理器 -->
- <bean id="transactionManager"
- class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource"></property>
- </bean>
- <!-- 启用事务注解 -->
- <tx:annotation-driven transaction-manager="transactionManager"/>
- </beans>
在service的业务方法分钟添加注解
- package com.atguigu.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;
- @Transactional
- @Override
- public void purchase(String username, String isbn) {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {}
- //1. 获取书的单价
- int price = bookShopDao.findBookPriceByIsbn(isbn);
- //2. 更新数的库存
- bookShopDao.updateBookStock(isbn);
- //3. 更新用户余额
- bookShopDao.updateUserAccount(username, price);
- }
- }
这样我们就达到了事务管理的操作
上面一个最简单的事务操作已经完成了,接下面我们进一步研究事务的隔离级别、事务的过期时间、事务的传播行为等进行研究
什么叫事务传播行为?听起来挺高端的,其实很简单。
即然是传播,那么至少有两个东西,才可以发生传播。单体不存在传播这个行为。
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。
Spring定义了七种传播行为:
接下来我们通过代码来实现,我们再定义一个接口,让一个人可以购买多本书
- package com.atguigu.spring.tx;
- import java.util.List;
- public interface Cashier {
- public void checkout(String username, List<String> isbns);
- }
- package com.atguigu.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);
- }
- }
- }
一个人购买多本书的方法checkout使用了 @Transactional标注,说明该方法是一个事务方法,然后该方法调用了bookShopService.purchase,这个方法也是事务方法,一个事务方法调用了另外一个事务方法
我们来看下测试方法
- package com.atguigu.spring.tx;
- import static org.junit.Assert.*;
- import java.util.Arrays;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class SpringTransactionTest {
- 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 testTransactionlPropagation(){
- cashier.checkout("AA", Arrays.asList("1001", "1002"));
- }
- }
我们来看下下面的业务操作过程
如果采用required特性,在购买1001这本书的时候购买成功了,但是在购买1002的时候抛出了异常,这个时候因为采用的是required特性,这个时候购买书的操作都使用checkout这个方法的事务中,抛出异常
会对事务的全部操作进行回滚,但是购买的1001成功的操作也会回滚,导致一本书都不会购买成功
接下来第二种事务
如果采用required-new特性,在购买1001这本书的时候购买成功了,但是在购买1002的时候抛出了异常,这个时候因为采用的是required-new特性,这个时候在purches中会开启一个新的事务
只会对异常操作的事务进行回滚,购买的1001成功的操作不会回滚,后面1001会购买成功,不会回滚,只会回滚购买1002的操作
制定required-new特性需要对serivice进行配置
- package com.atguigu.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;
- @Transactional(propagation = Propagation.REQUIRES_NEW)
- @Override
- public void purchase(String username, String isbn) {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {}
- //1. 获取书的单价
- int price = bookShopDao.findBookPriceByIsbn(isbn);
- //2. 更新数的库存
- bookShopDao.updateBookStock(isbn);
- //3. 更新用户余额
- bookShopDao.updateUserAccount(username, price);
- }
- }
对于事务的操作一般掌握上面的两种就可以了
接下来我们学校事务的隔离级别
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(序列化):代价最高最可靠的隔离级别(该隔离级别能防止脏读、不可重复读、幻读)
丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的;
幻读:同样的事务操作过程中,不同时间段多次(不同事务)读取同一数据,读取到的内容不一致(一般是行数变多或变少)。
脏读:一个事务读取到另外一个未提及事务的内容,即为脏读。
不可重复读:同一事务中,多次读取内容不一致(一般行数不变,而内容变了)。
幻读与不可重复读的区别:幻读的重点在于插入与删除,即第二次查询会发现比第一次查询数据变少或者变多了,以至于给人一种幻象一样,而不可重复读重点在于修改,即第二次查询会发现查询结果比第一次查询结果不一致,即第一次结果已经不可重现了。
数据库隔离级别越高,执行代价越高,并发执行能力越差,因此在实际项目开发使用时要综合考虑,为了考虑并发性能一般使用提交读隔离级别,它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁或乐观锁来解决这些问题。
- package com.atguigu.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})
- @Transactional(propagation=Propagation.REQUIRES_NEW,
- isolation=Isolation.READ_COMMITTED,
- readOnly=false,
- timeout=3)
- @Override
- public void purchase(String username, String isbn) {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {}
- //1. 获取书的单价
- int price = bookShopDao.findBookPriceByIsbn(isbn);
- //2. 更新数的库存
- bookShopDao.updateBookStock(isbn);
- //3. 更新用户余额
- bookShopDao.updateUserAccount(username, price);
- }
- }
上面我们讲解了通过注解的方式来管理事务,接下面我们通过xml的方式来进行事务的管理
工程的代码如下:
- package com.atguigu.spring.tx.xml;
- import org.springframework.jdbc.core.JdbcTemplate;
- public class BookShopDaoImpl implements BookShopDao {
- private JdbcTemplate jdbcTemplate;
- public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
- this.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);
- }
- }
- package com.atguigu.spring.tx.xml;
- public interface BookShopDao {
- //根据书号获取书的单价
- public int findBookPriceByIsbn(String isbn);
- //更新数的库存. 使书号对应的库存 - 1
- public void updateBookStock(String isbn);
- //更新用户的账户余额: 使 username 的 balance - price
- public void updateUserAccount(String username, int price);
- }
- package com.atguigu.spring.tx.xml.service;
- public interface BookShopService {
- public void purchase(String username, String isbn);
- }
- package com.atguigu.spring.tx.xml.service;
- import java.util.List;
- public interface Cashier {
- public void checkout(String username, List<String> isbns);
- }
- package com.atguigu.spring.tx.xml.service.impl;
- import com.atguigu.spring.tx.xml.BookShopDao;
- import com.atguigu.spring.tx.xml.service.BookShopService;
- public class BookShopServiceImpl implements BookShopService {
- private BookShopDao bookShopDao;
- public void setBookShopDao(BookShopDao bookShopDao) {
- this.bookShopDao = bookShopDao;
- }
- @Override
- public void purchase(String username, String isbn) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {}
- //1. 获取书的单价
- int price = bookShopDao.findBookPriceByIsbn(isbn);
- //2. 更新数的库存
- bookShopDao.updateBookStock(isbn);
- //3. 更新用户余额
- bookShopDao.updateUserAccount(username, price);
- }
- }
- package com.atguigu.spring.tx.xml.service.impl;
- import java.util.List;
- import com.atguigu.spring.tx.xml.service.BookShopService;
- import com.atguigu.spring.tx.xml.service.Cashier;
- public class CashierImpl implements Cashier {
- private BookShopService bookShopService;
- public void setBookShopService(BookShopService bookShopService) {
- this.bookShopService = bookShopService;
- }
- @Override
- public void checkout(String username, List<String> isbns) {
- for(String isbn: isbns){
- bookShopService.purchase(username, isbn);
- }
- }
- }
我们来看看最为关键的代码
applicationContext-tx-xml.xml
- <?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:tx="http://www.springframework.org/schema/tx"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
- <context:component-scan base-package="com.atguigu.spring"></context:component-scan>
- <!-- 导入资源文件 -->
- <context:property-placeholder location="classpath:db.properties"/>
- <!-- 配置 C3P0 数据源 -->
- <bean id="dataSource"
- class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="user" value="${jdbc.user}"></property>
- <property name="password" value="${jdbc.password}"></property>
- <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
- <property name="driverClass" value="${jdbc.driverClass}"></property>
- <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
- <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
- </bean>
- <!-- 配置 Spirng 的 JdbcTemplate -->
- <bean id="jdbcTemplate"
- class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="dataSource"></property>
- </bean>
- <!-- 配置 bean -->
- <bean id="bookShopDao" class="com.atguigu.spring.tx.xml.BookShopDaoImpl">
- <property name="jdbcTemplate" ref="jdbcTemplate"></property>
- </bean>
- <bean id="bookShopService" class="com.atguigu.spring.tx.xml.service.impl.BookShopServiceImpl">
- <property name="bookShopDao" ref="bookShopDao"></property>
- </bean>
- <bean id="cashier" class="com.atguigu.spring.tx.xml.service.impl.CashierImpl">
- <property name="bookShopService" ref="bookShopService"></property>
- </bean>
- <!-- 1. 配置事务管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource"></property>
- </bean>
- <!-- 2. 配置事务属性 -->
- <tx:advice id="txAdvice" transaction-manager="transactionManager">
- <tx:attributes>
- <!-- 根据方法名指定事务的属性 -->
- <tx:method name="purchase" propagation="REQUIRES_NEW"/>
- <tx:method name="get*" read-only="true"/>
- <tx:method name="find*" read-only="true"/>
- <tx:method name="*"/>
- </tx:attributes>
- </tx:advice>
- <!-- 3. 配置事务切入点, 以及把事务切入点和事务属性关联起来 -->
- <aop:config>
- <aop:pointcut expression="execution(* com.atguigu.spring.tx.xml.service.*.*(..))"
- id="txPointCut"/>
- <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
- </aop:config>
- </beans>
注意点:jdbc对应的事务管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager
mybatis这里就有进行修改
上面有几个点需要特别注意
1、事务传播特性的时候:两个事务是在不同的service中,CashierImpl中checkout方法中添加了事务
然后在BookShopServiceImpl类的purchase方法中添加了事务,checkout方法调用了purchase方法,不同service中的事务相互调用
2、如果是同一个service中的两个事务方法会存在这样的问题了?后面会分析,在开发的时候要避免一个service中两个事务方法相互调用,因为会存在很多问题
相当的经典
上面对于的代码在网上下载:
尚硅谷_佟刚_Spring_完整代码
尚硅谷spring 事物管理的更多相关文章
- MyBatis6:MyBatis集成Spring事物管理(下篇)
前言 前一篇文章<MyBatis5:MyBatis集成Spring事物管理(上篇)>复习了MyBatis的基本使用以及使用Spring管理MyBatis的事物的做法,本文的目的是在这个的基 ...
- Spring事物管理--相关要点及配置事物管理器
事务的四大特征 1.原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做要么全不做 2.一致性:数据不会因为事务的执行而遭到破坏 3.隔离性:一个事物的执行,不受其他事务的干扰,即并 ...
- spring 事物管理没起到作用
今天在做项目的时候发现配置的spring 事物管理没起到作用.可是配置又是依据官网配置的,不可能会错.最后发现使mysql的问题 普通情况下,mysql会默认提供多种存储引擎,你能够通过以下的查看: ...
- java陷阱之spring事物管理导致锁无效
模拟锁情况无效 1.创建一个表 SET NAMES utf8mb4; ; DROP TABLE IF EXISTS `demo`; CREATE TABLE `demo` ( `id` ) NOT N ...
- Spring事物管理简介 (转)
一.事物1.什么是事物 事物指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败 2.事物的特性 原子性:事物是一个不可分割的工作单位,事物中的操作要么都发生,要么都不发生 一致性:事物前后数据 ...
- 20191003 尚硅谷Spring Cloud教学视频
视频信息 视频日期:2018-4-19 讲师:尚硅谷周阳 Spring Cloud版本:Dalston.RELEASE 当前版本:Greenwich SR3 微服务.微服务架构.Spring Clou ...
- 集成Spring事物管理
什么是事物 事物是访问数据库的一个操作序列,数据库应用系统通过事物集来完成对数据库的存取.事物的正确执行使得数据库从一种状态转换为另一种状态. 事物必须服从ISO/IEC所制定的ACID原则.ACID ...
- Spring事物管理机制简单学习
首先spring并不直接管理事物,而是提供了多种事物管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现. Spring管理事物的接口是org.s ...
- 【JAVA】Spring 事物管理
在Spring事务管理中通过TransactionProxyFactoryBean配置事务信息,此类通过3个重要接口完成事务的配置及相关操作,分别是PlatformTransactio ...
随机推荐
- Linux显示行号设置
linux显示行号设置 第一步,打开vim vi ~/.vimrc 第二步,在该文件中加入一行,命令如下: set nu # 显示行号 set nonu # 不显示行号 微信公众号:喵哥解说 公众号介 ...
- super调用父类的属性方法
super:可以用来修饰属性 方法 构造器 当子类与父类中有同名的属性时,可以通过 super.此属性 显式的调用父类声明的属性 若想调用子类的同名的属性“this.此属性” 2.当子类重 ...
- group by和having注意事项
执行和编写顺序:join->where->group by->having 其中where与having的区别: where用于在group by分组之前过滤掉某些行, group ...
- 电子邮件协议及GO发送QQ邮件
目录 一.电子邮件的工作机制 1.1 SMTP 1.2 POP3 1.3 IMAP 二.邮件地址 三.MIME信息 四.使用golang发送qq邮件 一.电子邮件的工作机制 提供电子邮件服务的协议叫做 ...
- ES6-for...of与for...in
1.includes 数组是否包含某个东西 <!DOCTYPE html> <html lang="en"> <head> <meta c ...
- Java实现 LeetCode 832 翻转图像(位运算)
832. 翻转图像 给定一个二进制矩阵 A,我们想先水平翻转图像,然后反转图像并返回结果. 水平翻转图片就是将图片的每一行都进行翻转,即逆序.例如,水平翻转 [1, 1, 0] 的结果是 [0, 1, ...
- (Java实现) 组合的输出
问题 B: [递归入门]组合的输出 时间限制: 1 Sec 内存限制: 128 MB 题目描述 排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r < = n),我们 ...
- Java实现 蓝桥杯 基础练习 特殊的数字
基础练习 特殊的数字 时间限制:1.0s 内存限制:512.0MB 提交此题 锦囊1 锦囊2 问题描述 153是一个非常特殊的数,它等于它的每位数字的立方和,即153=111+555+333.编程求所 ...
- C++实现网络寻路
标题:网络寻路 X 国的一个网络使用若干条线路连接若干个节点.节点间的通信是双向的.某重要数据包,为了安全起见,必须恰好被转发两次到达目的地.该包可能在任意一个节点产生,我们需要知道该网络中一共有多少 ...
- java实现第六届蓝桥杯星系炸弹
星系炸弹 题目描述 在X星系的广袤空间中漂浮着许多X星人造"炸弹",用来作为宇宙中的路标. 每个炸弹都可以设定多少天之后爆炸. 比如:阿尔法炸弹2015年1月1日放置,定时为15天 ...