日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

spring 事务隔离级别和传播行为_Java工程师面试1000题146-Spring数据库事务传播属性和隔离级别...

發布時間:2024/9/27 java 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring 事务隔离级别和传播行为_Java工程师面试1000题146-Spring数据库事务传播属性和隔离级别... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

146、簡介一下Spring支持的數據庫事務傳播屬性和隔離級別

介紹Spring所支持的事務和傳播屬性之前,我們先了解一下SpringBean的作用域,與此題無關,僅做一下簡單記錄。

在Spring中,可以在元素的scope屬性中設置bean的作用域,來決定這個bean是單實例的還是多實例的。默認情況下,Spring只為每個在IOC容器里聲明的bean創建唯一的實例,整個IOC容器范圍內都可以共享該實例;所有后續的getBean()調用和bean引用都將返回這個唯一的bean實例,該作用域被稱為singleton,他是所有bean的默認作用域。

  • singleton:在SpringIOC容器中僅存在一個bean實例,Bean以單實例的方式存在
  • prototype:每次調用getBean()時都會返回一個新的實例
  • request:每次HTTP請求都會創建一個新的Bean。該作用域僅適用于WebApplicationContext環境。
  • session:同一個HTTP Session共享一個Bean,不同的HTTP Session使用不同的Bean。該作用域僅適用于WebApplicationContext環境。
  • 介紹完Spring Bean的作用域之后,下面開始進入正題——Spring支持的數據庫事務傳播屬性和隔離級別

    1、事務的傳播屬性

    首先我們先了解一下什么是事務的傳播屬性(傳播行為):當一個事務方法被被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啟一個新事務,并在自己的事務中運行。

    事務的傳播行為是由傳播屬性來指定的。

    propagation:用來設置事務的傳播行為:一個方法運行在了一個開啟了事務的方法中時,當前方法是使用原來的事務,還是開啟一個新的事務,這就是事務的傳播行為。

    比如:Propagation.REQUIRED:默認值,代表繼續使用原來的事務;Propagation.REQUIRES_NEW:將原來的事務掛起,開啟一個新的事務。最常用的事務傳播屬性就是REQUIRED和REQUIRES_NEW,下面就通過編程來進行測試。

    首先,在數據庫里面新建三張表:

    CREATE DATABASE /*!32312 IF NOT EXISTS*/`location` /*!40100 DEFAULT CHARACTER SET utf8 */;USE `location`;DROP TABLE IF EXISTS `account`;CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(30) DEFAULT NULL, `balance` float unsigned DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;insert into `account`(`id`,`username`,`balance`) values (1,'HanZong',100);DROP TABLE IF EXISTS `book`;CREATE TABLE `book` ( `isbn` varchar(20) DEFAULT NULL, `name` varchar(20) DEFAULT NULL, `price` float DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into `book`(`isbn`,`name`,`price`) values ('1001','Spring',60),('1002','SpringMVC',50);DROP TABLE IF EXISTS `book_stock`;CREATE TABLE `book_stock` ( `isbn` varchar(20) DEFAULT NULL, `stock` int(11) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into `book_stock`(`isbn`,`stock`) values ('1001',100),('1002',100);

    然后,搭建Spring的開發環境,具體配置在這里不再講解了,不是本知識點的重點。然后新建三個接口,三個實現類。

    Cashier接口:

    import java.util.List;public interface Cashier { //去結賬的方法 void checkout(int userId, List isbns);}實現類:import com.spring.transaction.BookShopService;import com.spring.transaction.Cashier;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service("cashier")public class CashierImpl implements Cashier { @Autowired private BookShopService bookShopService; @Transactional @Override public void checkout(int userId, List isbns) { for (String isbn : isbns){ //調用BookShopService中的買東西方法 bookShopService.purchase(userId,isbn); } }}BookShopService接口:public interface BookShopService { //定義一個買東西方法 void purchase(int userId,String isbn);}

    實現類:

    import com.spring.transaction.BookShopDao;import com.spring.transaction.BookShopService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;@Servicepublic class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; @Transactional @Override public void purchase(int userId, String isbn) { //1.獲取要買的圖書的價格 double bookPrice = bookShopDao.getBookPriceByIsbn(isbn);System.out.println(bookPrice); //2.更新圖書的庫存 bookShopDao.updateBookStock(isbn); //3.更新用戶的余額 bookShopDao.updateAccountBalance(userId, bookPrice);double bookPriceByIsbn = bookShopDao.getBookPriceByIsbn(isbn);System.out.println(bookPriceByIsbn); }}

    操作數據庫的接口:

    public interface BookShopDao {//根據書號查詢圖書的價格double getBookPriceByIsbn(String isbn);//根據書號更新圖書的庫存,每次只買一本圖書void updateBookStock(String isbn);//根據用戶的id和圖書的價格更新用戶的賬戶余額void updateAccountBalance(int userId, double bookPrice);}

    實現類:

    import com.spring.transaction.BookShopDao;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 {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic double getBookPriceByIsbn(String isbn) {// 寫sql語句String sql = "select price from book where isbn = ?";// 調用JdbcTemplate中的queryForObject方法Double bookPrice = jdbcTemplate.queryForObject(sql, Double.class, isbn);return bookPrice;}@Overridepublic void updateBookStock(String isbn) {// 寫sql語句String sql = "update book_stock set stock = stock - 1 where isbn = ?";// 調用JdbcTemplate中的update方法jdbcTemplate.update(sql, isbn);}@Overridepublic void updateAccountBalance(int userId, double bookPrice) {// 寫sql語句String sql = "update account set balance = balance - ? where id = ?";// 調用JdbcTemplate中的update方法jdbcTemplate.update(sql, bookPrice, userId);}}

    介紹一下上面的接口和實現類,BookShopService接口里面有一個買東西的方法purchase(),Cashier里面有一個checkout()方法,結賬的方法,checkout()方法要調用purchase()方法來實現功能,checkout()方法上面添加了聲明式事務注解@Transactional,purchase()方法上面也添加了聲明式事務注解@Transactional。checkout()方法調用了purchase()方法,兩個方法都使用了事務,這時候在運行的時候,purchase()到底是使用自己的事務呢,還是使用checkout()的事務呢?這個就屬于事務的傳播行為!

    事務的傳播行為可以使用@Transactional注解里面的一個propagation屬性來設置。propagation可以設置以下7種屬性值。

    我們來看一下啊,purchase()方法運行在checkout()方法里面,按照Spring默認的事務傳播屬性為REQUIRED,那么purchase()方法就應該使用checkout()方法的事務,checkout()方法里面有一個for循環,可能會調用多次purchase方法,根據事務的原子性,多次執行purchase()方法要么全部成功,要么全部失敗。我們寫一個測試方法:

    public class TestTX { //創建IOC容器對象 ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); @Test public void testCashier(){ Cashier cashier = (Cashier) ioc.getBean("cashier"); //創建List List isbns = new ArrayList<>(); isbns.add("1001"); isbns.add("1002"); //去結賬 cashier.checkout(1,isbns); }}

    測試程序中,我們創建一個ArrayList,里面添加兩個圖書id,一個是1001,一個是1002,代表我們將要購買的圖書,圖書的價格保存在數據庫中book表中,1001的價格是60,1002的價格是50,另一張表book_store里面存放的是圖書庫存,1001庫存100本,1002庫存100本,最后一張表是用戶表account,里面就只有一個用戶,用戶余額100元,這個余額在建表的時候必須要設置為unsigned的,不能成為負數,否則就沒法測試了。

    現在是賬戶余額只有100元,要同時買兩本1001和1002,明顯差10元。現在我們來測試一下,到底是一本也買不成功,還是可以買成功一本。根據事務傳播行為,沒有設置就代表默認值,默認值就是REQUIRED:如果有事務在運行,當前的方法就在這個事務內運行,否則就開啟一個新的事務,并在自己的事務內運行。也就是說,現在買1001和買1002在同一個事務里面,根據事務的原子性,要么都完成,要么都不完成,現在我的余額是100,可以買成功1001,不能賣成功1002,到底最終的結果是什么呢?讓我們運行測試程序。報了一個異常:

    Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Out of range value for column 'balance' at row 1

    這句報異常就是由于在建表的時候把balance設置為unsigned的,使之不能成為負數。這不是我們關心的,我們關心的是數據庫中庫存和賬戶余額是否發生變化。我們刷新數據庫表,發現余額沒有改變,兩本書都沒有買成功。為什么會這樣呢?我們再來分析一下。如果事務的傳播行為是默認值的話,即我們沒有在@Transactional注解里面設置,默認值就是REQUIRED,也就說是會使用checkout()方法原來的事務,雖然我們在purchase()上面也添加了事務,但是由于事務的傳播行為是默認值,所以他會使用checkout()方法的事務,如果使用checkout()方法的事務,我們發現,在ArrayList里面有兩本圖書,買兩本書調用的都是同一個purchase()方法,兩次調用是在同一個事務里面,但是買完1001之后,再去買1002,失敗了,根據事務的原子性,要么都完成,要么都不完成,所以,它要回滾事務,最終才造成了上面的結果。

    那么,我們能不能讓它買成功一本呢?可以,只需要把purchase()方法的事務傳播行為改為REQUIRES_NEW。

    @Transactional(propagation = Propagation.REQUIRES_NEW)

    同樣運行測試程序,還是報“Data truncation: Out of range value for column 'balance' at row 1”異常,不管他,我們刷新數據庫,觀察賬戶余額,發現變為了40,再看一下庫存,1001的庫存變為了99,1002的庫存沒有變還是100。這就說明我們買成功了一本。由于我們把purchase()方法的事務傳播行為改為REQUIRES_NEW,就是每次調用都要開啟一個新事物,雖然checkout()也設置了事務,但是我不用你的,每次都用我自己的,這就是事務之間的隔離性,互相之間沒有影響,所以我們買1001和買1002的時候用到的就不是同一個事務了,購買1002失敗不會導致購買1001也失敗。所以最終的結果就是1001買成功了,1002沒有買成功。

    小總結:

    • REQUIRED傳播行為:當bookService的purchase()方法被另外一個事務方法checkout()調用時,它會默認在現有的事務內運行。因此在checkout()方法的開始和結束內只有一個事務,這個事務只會在checkout()方法調用結束時被提交,那就導致用戶一本都買不了。
    • REQUIRES_NEW傳播行為:表示該方法必須啟動一個新的事務,并在自己的事務內運行,如果已經有在運行,就先把他掛起。

    ?

    2、事務的隔離級別

    在講事務的隔離級別之前,我們先來看一下數據庫事務并發問題:

    假設現在有兩個事務:Transaction01和Transaction02并發執行。

    ①臟讀:當前事務讀到了其他事務更新但是還沒有提交的值(其他事務不回滾還好,其他事務回滾你讀到的就是一個無效值)。

  • Transaction01將某條記錄的AEG值從20修改為30
  • Transaction02讀取了Transaction01更新后的值:30
  • Transaction01回滾事務,AEG的值又恢復到了20
  • Transaction02讀取到的30就是一個無效的值
  • ②不可重復讀:

  • Transaction01讀取了AEG的值為20
  • Transaction02將AEG的值修改為30
  • Transaction01再次讀取AEG值為30,和第一次讀取結果不一致
  • ③幻讀:

  • Transaction01讀取了STUDENT表中的一部分數據
  • Transaction02向STUDENT表中插入了新的行
  • 事務的隔離級別:數據庫系統必須具有隔離并發運行各個事務的能力,使它們不會相互影響,避免各種并發問題。一個事務與其他事務隔離的程度成為事務的隔離級別。SQL標準中規定了多種事務隔離級別,不同隔離級別對應不同的干擾程度,隔離級別越高,數據一致性就越好,但是并發性就越弱。

    1、讀未提交:READ UNCOMMITTED,允許Transaction01讀取Transaction02未提交的修改。(臟讀、不可重復讀、幻讀都有可能出現)

    2、讀已提交:READ COMMITTED,要求Transaction01只能讀取Transaction02已經提交的修改。(臟讀就可以避免了)

    3、可重復讀:REPEATABLE READ,確保Transaction01可以多次從一個字段讀取到相同的值,即Transaction01執行期間禁止其他事務對這個字段進行更新。(臟讀、不可重復讀都不會出現了)

    4、串行化:SERIALIZABLE,確保Transaction01可以多次從一個表中讀取到相同的行,在Transaction01執行期間,禁止其他事務對這個表進行添加、更新、刪除操作??梢员苊馑胁l問題,但是性能最低。(臟讀、不可重復讀、幻讀都不可能出現)

    各數據庫產品對事務隔離級別的支持程度:

    總結

    以上是生活随笔為你收集整理的spring 事务隔离级别和传播行为_Java工程师面试1000题146-Spring数据库事务传播属性和隔离级别...的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 亚洲一区二区在线电影 | 麻豆天天躁天天揉揉av | 国内精品久久久久久久久 | 中文字幕日本 | 91传媒在线视频 | 久久黄色录像 | 国产91精选 | 91亚洲欧美 | 北条麻妃一区二区三区免费 | 伊人综合影院 | 国产深喉视频一区二区 | 欧美日韩精品免费 | 国产视频麻豆 | 日韩精品成人一区 | 亚洲激情午夜 | 91精品国产精品 | 午夜有码 | 国产精品99无码一区二区 | 得得的爱在线视频 | 精品福利一区二区 | 久久久久久网 | 日韩欧美在线观看一区二区三区 | 超碰在线香蕉 | 久久av资源 | 乖女从小调教h尿便器小说 欧美韩一区二区 | 在线观看日韩av电影 | 精品视频免费观看 | 欧美精品福利 | www.狠狠干 | 亚洲另类图区 | 无套在线观看 | 欧美日韩国内 | 久色网| 日韩毛片网 | 国产成人无码性教育视频 | 亚洲国产婷婷香蕉久久久久久99 | 麻豆av一区 | 日韩精品在线观看一区二区 | 91久久久久久久久久 | 加勒比视频在线观看 | 国产一级做a | 国产农村妇女精品一二区 | 日本福利视频导航 | 久久久久久久久综合 | av噜噜噜| 国内偷拍一区 | 视频在线观看免费大片 | 操伊人| 久久久国产网站 | 成人福利一区 | 激情久久久久久久 | 少妇被狂c下部羞羞漫画 | 国产乱码精品一区二区三 | 欧美深性狂猛ⅹxxx深喉 | 日韩网红少妇无码视频香港 | 欧美成人一二三 | 毛片哪里看 | videos麻豆 | 精品免费在线观看 | 三级国产在线观看 | 成人激情开心网 | 午夜寻花| 亚洲av永久中文无码精品综合 | 超碰超碰超碰超碰超碰 | 欧美午夜性春猛交 | 毛片毛片 | 国产精品一区免费 | 中文字幕在线观看av | 91资源在线观看 | 午夜欧美精品 | 中文字幕+乱码+中文乱码www | 欧美精品黑人 | 久久免费的精品国产v∧ | 中文字幕av日韩 | 国产欧美精品在线 | 欧美性生活一区二区 | 内裤摩擦1v1h | 紧身裙女教师三上悠亚红杏 | 国产成人精品一区二区三区免费 | 久久久精品国产免费爽爽爽 | 欧美成人免费高清视频 | 一区二区在线视频免费观看 | 丝袜av在线播放 | 成人αv| 久久a久久| 久久99久久99精品免观看软件 | 七七久久 | 欧美国产精品一区二区三区 | 天天色天天射综合网 | 日本护士╳╳╳hd少妇 | 年下总裁被打光屁股sp | 成人久久精品人妻一区二区三区 | 日韩欧美一二三 | 黄色在线观看国产 | 经典三级第一页 | ass极品水嫩小美女ass | 色婷婷小说| 亚洲成人91 | 亚洲黄色一区二区三区 |