数据库事务的传播
数据库事务的传播:数据库事务的传播就是当创建的方法运行在一个事务当中,而该方法内部调用的其他方法同样具备事务属性,其他方法的事务该如何启动。
下面举例
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);
}