【面试】数据库事务的传播和事务的隔离属性

数据库事务的传播

数据库事务的传播:数据库事务的传播就是当创建的方法运行在一个事务当中,而该方法内部调用的其他方法同样具备事务属性,其他方法的事务该如何启动。

下面举例

BookShopServiceImpl.java

@Transactional()
@Override
public void purchase(int userId,String isbn){
	double bookPrice=bookShopDao.getBookPriceByIsbn(isbn);
	bookShopDao.updateBookStock(isbn);
	bookShopDao.updateAccountBalance(userId,bookPrice);
	
}

cashier.java

@AutoWired
private BookShopService bookShopService;

@Transactional()
@Override
public void checkout(int userId,List<String> isbns){
	for(String isbn: isbns){
		bookShopService.purchase(userId,isbn);
	}
}

test.java

@AutoWired
private cashier cashier ;

@Transactional()
@Override
public void testCashier(){
	List<String> isbns=new List<String>;
	isbns.add("1001");
	isbns.add("1002");
	cashier.checkout(1,isbns);
}

调用test.java当中的testCashier() 方法,而testCashier() 又调用了checkout() 方法,而checkout() 方法调用了事务,

事务是在方法没有彻底完成之前其中对于数据库的改动都是临时的,并没有真正写入库中。如果方法执行到后面报错了,前面对于数据库的修改都会回滚到事务发生之前的状态。

而checkout() 方法当中又调用了purchase() 方法。而purchase() 方法也是事务方法。

那么就出现了一个问题,是把purchase() 方法放到checkout的事务中一起执行呢,还是创建一个新的事务来给purchase() 方法执行呢?

数据库中默认是把purchase() 方法放到checkout方法的事务当中执行。叫做required

BookShopServiceImpl.java

@Transactional(propagation=Propagation.REQUIRE)  //默认的transaction的propagation是require
@Override
public void purchase(int userId,String isbn){
	double bookPrice=bookShopDao.getBookPriceByIsbn(isbn);
	bookShopDao.updateBookStock(isbn);
	bookShopDao.updateAccountBalance(userId,bookPrice);
	
}

而还有一种常用的事务传播,是required_new,是当调用到这个方法的时候,如果当前没有事务的话,就创建新事务,如果当前有事务正在运行,那么把正在运行的事务先暂时挂起,创建一个新的事务来给purchase() 方法运行。purchase() 方法事务运行完毕之后再把挂起的事务激活。
BookShopServiceImpl.java

@Transactional(propagation=Propagation.REQUIRES_NEW)  //默认的transaction的propagation是require
@Override
public void purchase(int userId,String isbn){
	double bookPrice=bookShopDao.getBookPriceByIsbn(isbn);
	bookShopDao.updateBookStock(isbn);
	bookShopDao.updateAccountBalance(userId,bookPrice);
	
}

下面是使用REQUIRE_NEW的事务运行情况
事务运行情况
还有其他类型的事务传播模式,但是使用的频率没有上面两种要高。可以自行查阅
事务的类别

事务的隔离属性

在事务的运行当中会发生脏读,不可重复读,幻读这三种事务的并发问题。

  • 脏读:比如说事务1修改了数据库当中的某一项值从 20 修改为 30 ,之后事务2读取了这一项值30,但是事务1发生错误数据回滚从30有变成20;这样数据就发生了不一致,称之为脏读。
  • 不可重复读:事务1先读取数据库当中的值20,然后事务2再修改把20变成30,然后事务1在读取值为30,发生了数据不一致的情况
  • 幻读:幻读是事务1先读取数据库的一部分的行,之后事务2往数据库当中又插入很多新的行,事务1重新读取的时候发现多出很多新的行,就是幻读

那么该怎么解决这些事务并发所产生的问题呢:有四种解决方法。读未提交,读已提交,可重复读,串行化。

  • 读未提交(READ UNCOMMITED):事务1可以读到事务2还没有提交到数据库的数据,这种方法不能防止上面任何一种并行事务情况的发生
  • 读已提交(READ COMMITED):事务1只能读到事务2提交到数据库当中的数据
  • 可重复读(REPEATABLE READ):事务在读这个数据的时候,直到事务结束之前,数据库被读取的数据都不能发生改变。
  • 串行化(SERIALIZABLE):可以读到相同的行,事务在读取这个行时直到事务结束,其他事务不允许对读取的表进行添加,更改,删除操作。

而这些操作可以解决上述事务并行发生的哪些问题呢?

  • 读未提交不能解决任何并行问题
  • 读已提交可以解决脏读这个问题但是不可重复读和幻读不能解决(读已提交的应用最广,因为解决了脏读,性能也有保障)
  • 可重复读可以解决脏读和不可重复读两个事务的并发问题,但是幻读不能解决(而且可重复读相当于对一条数据上锁,运行效率上会发生略微损失)
  • 串行化可以解决脏读和不可重复读以及幻读,但是串行化就相当于给整个表上了锁,业务量大起来运行起来的效率大大降低。

在这里插入图片描述

@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.DEFAULT)  //默认mysql的transaction的isolation的默认是可重复读,而oracle的默认是读已提交。
@Override
public void purchase(int userId,String isbn){
	double bookPrice=bookShopDao.getBookPriceByIsbn(isbn);
	bookShopDao.updateBookStock(isbn);
	bookShopDao.updateAccountBalance(userId,bookPrice);
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值