Spring聲明式事務(wù)配置使用詳解
序章
Spring 框架對(duì) JDBC 進(jìn)行封裝,使用 JdbcTemplate 方便實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)操作
準(zhǔn)備工作
<dependencies> <!-- 基于Maven依賴(lài)傳遞性,導(dǎo)入spring-context依賴(lài)即可導(dǎo)入當(dāng)前所需所有jar包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.1</version> </dependency> <!-- Spring 持久化層支持jar包 --> <!-- Spring 在執(zhí)行持久化層操作、與持久化層技術(shù)進(jìn)行整合過(guò)程中,需要使用orm、jdbc、tx三個(gè) jar包 --> <!-- 導(dǎo)入 orm 包就可以通過(guò) Maven 的依賴(lài)傳遞性把其他兩個(gè)也導(dǎo)入 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.3.1</version> </dependency> <!-- Spring 測(cè)試相關(guān) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.1</version> </dependency> <!-- junit測(cè)試 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- MySQL驅(qū)動(dòng) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency> <!-- 數(shù)據(jù)源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.31</version> </dependency> </dependencies>
創(chuàng)建jdbc.properties
jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.driver=com.mysql.cj.jdbc.Driver
配置Spring的配置文件
<!--導(dǎo)入外部屬性文件--> <context:property-placeholderlocation="classpath:jdbc.properties"/> <!--配置數(shù)據(jù)源--> <beanid="druidDataSource"class="com.alibaba.druid.pool.DruidDataSource"> <propertyname="url"value="${atguigu.url}"/> <propertyname="driverClassName"value="${atguigu.driver}"/> <propertyname="username"value="${atguigu.username}"/> <propertyname="password"value="${atguigu.password}"/> </bean> <!--配置JdbcTemplate--> <beanid="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate"> <!--裝配數(shù)據(jù)源--> <propertyname="dataSource"ref="druidDataSource"/> </bean>
聲明式事務(wù)概念
編程式的實(shí)現(xiàn)方式存在缺陷:
細(xì)節(jié)沒(méi)有被屏蔽:具體操作過(guò)程中,所有細(xì)節(jié)都需要程序員自己來(lái)完成,比較繁瑣。
代碼復(fù)用性不高:如果沒(méi)有有效抽取出來(lái),每次實(shí)現(xiàn)功能都需要自己編寫(xiě)代碼,代碼就沒(méi)有得到復(fù) 用。
聲明式事務(wù)
既然事務(wù)控制的代碼有規(guī)律可循,代碼的結(jié)構(gòu)基本是確定的,所以框架就可以將固定模式的代碼抽取出來(lái),進(jìn)行相關(guān)的封裝。
封裝起來(lái)后,我們只需要在配置文件中進(jìn)行簡(jiǎn)單的配置即可完成操作。
好處1:提高開(kāi)發(fā)效率
好處2:消除了冗余的代碼
好處3:框架會(huì)綜合考慮相關(guān)領(lǐng)域中在實(shí)際開(kāi)發(fā)環(huán)境下有可能遇到的各種問(wèn)題,進(jìn)行了健壯性、性 能等各個(gè)方面的優(yōu)化
代碼講解
配置 Spring 的配置文件
<!--掃描組件--> <context:component-scanbase-package="com.atguigu.spring.tx.annotation"> </context:component-scan> <!--導(dǎo)入外部屬性文件--> <context:property-placeholderlocation="classpath:jdbc.properties"/> <!--配置數(shù)據(jù)源--> <beanid="druidDataSource"class="com.alibaba.druid.pool.DruidDataSource"> <propertyname="url"value="${jdbc.url}"/> <propertyname="driverClassName"value="${jdbc.driver}"/> <propertyname="username"value="${jdbc.username}"/> <propertyname="password"value="${jdbc.password}"/> </bean> <!--配置JdbcTemplate--> <beanid="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate"> <!--裝配數(shù)據(jù)源--> <propertyname="dataSource"ref="druidDataSource"/> </bean>
創(chuàng)建表
REATETABLE`t_book`( book_id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'主鍵', `book_name`varchar(20)DEFAULTNULLCOMMENT'圖書(shū)名稱(chēng)', `price`int(11)DEFAULTNULLCOMMENT'價(jià)格', `stock`int(10)unsignedDEFAULTNULLCOMMENT'庫(kù)存(無(wú)符號(hào))', PRIMARYKEY(`book_id`) )ENGINE=InnoDBAUTO_INCREMENT=3DEFAULTCHARSET=utf8; insert into`t_book`(`book_id`,`book_name`,`price`,`stock`)values(1,'斗破蒼穹',80,100),(2,'斗羅大陸',50,100); CREATETABLE`t_user`( `user_id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'主鍵', `username`varchar(20)DEFAULTNULLCOMMENT'用戶(hù)名', `balance`int(10)unsignedDEFAULTNULLCOMMENT'余額(無(wú)符號(hào))', PRIMARYKEY(`user_id`) )ENGINE=InnoDBAUTO_INCREMENT=2DEFAULTCHARSET=utf8; insert into`t_user`(`user_id`,`username`,`balance`)values(1,'admin',50);
創(chuàng)建組件
創(chuàng)建BookController:
@Controller public class BookController { @Autowired private BookService bookService; public void buyBook(Integer bookId, Integer userId){ bookService.buyBook(bookId, userId); } }
創(chuàng)建接口BookService:
public interface BookService { void buyBook(Integer bookId, Integer userId); }
創(chuàng)建實(shí)現(xiàn)類(lèi)BookServiceImpl:
測(cè)試無(wú)事務(wù)情況
②模擬場(chǎng)景
用戶(hù)購(gòu)買(mǎi)圖書(shū),先查詢(xún)圖書(shū)的價(jià)格,再更新圖書(shū)的庫(kù)存和用戶(hù)的余額
假設(shè)用戶(hù)id為1的用戶(hù),購(gòu)買(mǎi)id為1的圖書(shū)
用戶(hù)余額為50,而圖書(shū)價(jià)格為80
購(gòu)買(mǎi)圖書(shū)之后,用戶(hù)的余額為-30,數(shù)據(jù)庫(kù)中余額字段設(shè)置了無(wú)符號(hào),因此無(wú)法將-30插入到余額字段
此時(shí)執(zhí)行sql語(yǔ)句會(huì)拋出SQLException
③觀察結(jié)果
因?yàn)闆](méi)有添加事務(wù),圖書(shū)的庫(kù)存更新了,但是用戶(hù)的余額沒(méi)有更新
顯然這樣的結(jié)果是錯(cuò)誤的,購(gòu)買(mǎi)圖書(shū)是一個(gè)完整的功能,更新庫(kù)存和更新余額要么都成功要么都失敗
加入事務(wù)
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 開(kāi)啟事務(wù)的注解驅(qū)動(dòng) 通過(guò)注解@Transactional所標(biāo)識(shí)的方法或標(biāo)識(shí)的類(lèi)中所有的方法,都會(huì)被事務(wù)管理器管理事務(wù) --> <!-- transaction-manager屬性的默認(rèn)值是transactionManager,如果事務(wù)管理器bean的id正好就 是這個(gè)默認(rèn)值,則可以省略這個(gè)屬性 --> <tx:annotation-driven transaction-manager="transactionManager" />
注意:導(dǎo)入的名稱(chēng)空間需要 tx 結(jié)尾的那個(gè)。
添加事務(wù)注解
因?yàn)閟ervice層表示業(yè)務(wù)邏輯層,一個(gè)方法表示一個(gè)完成的功能,因此處理事務(wù)一般在service層處理在BookServiceImpl的buybook()添加注解@Transactiona
觀察結(jié)果
由于使用了Spring的聲明式事務(wù),更新庫(kù)存和更新余額都沒(méi)有執(zhí)行
@Transactional注解標(biāo)識(shí)的位置
@Transactional標(biāo)識(shí)在方法上,咋只會(huì)影響該方法
@Transactional標(biāo)識(shí)的類(lèi)上,咋會(huì)影響類(lèi)中所有的方法
事務(wù)屬性:只讀
對(duì)一個(gè)查詢(xún)操作來(lái)說(shuō),如果我們把它設(shè)置成只讀,就能夠明確告訴數(shù)據(jù)庫(kù),這個(gè)操作不涉及寫(xiě)操作。這樣數(shù)據(jù)庫(kù)就能夠針對(duì)查詢(xún)操作來(lái)進(jìn)行優(yōu)化。
事務(wù)屬性:超時(shí)
事務(wù)在執(zhí)行過(guò)程中,有可能因?yàn)橛龅侥承﹩?wèn)題,導(dǎo)致程序卡住,從而長(zhǎng)時(shí)間占用數(shù)據(jù)庫(kù)資源。而長(zhǎng)時(shí)間占用資源,大概率是因?yàn)槌绦蜻\(yùn)行出現(xiàn)了問(wèn)題(可能是Java程序或MySQL數(shù)據(jù)庫(kù)或網(wǎng)絡(luò)連接等等)。
此時(shí)這個(gè)很可能出問(wèn)題的程序應(yīng)該被回滾,撤銷(xiāo)它已做的操作,事務(wù)結(jié)束,把資源讓出來(lái),讓其他正常程序可以執(zhí)行
概括來(lái)說(shuō)就是一句話:超時(shí)回滾,釋放資源。
事務(wù)屬性:回滾策略
聲明式事務(wù)默認(rèn)只針對(duì)運(yùn)行時(shí)異常回滾,編譯時(shí)異常不回滾。
可以通過(guò)@Transactional中相關(guān)屬性設(shè)置回滾策略
rollbackFor屬性:需要設(shè)置一個(gè)Class類(lèi)型的對(duì)象
rollbackForClassName屬性:需要設(shè)置一個(gè)字符串類(lèi)型的全類(lèi)名
noRollbackFor屬性:需要設(shè)置一個(gè)Class類(lèi)型的對(duì)象
rollbackFor屬性:需要設(shè)置一個(gè)字符串類(lèi)型的全類(lèi)名
事務(wù)屬性:事務(wù)隔離級(jí)別
數(shù)據(jù)庫(kù)系統(tǒng)必須具有隔離并發(fā)運(yùn)行各個(gè)事務(wù)的能力,使它們不會(huì)相互影響,避免各種并發(fā)問(wèn)題。一個(gè)事務(wù)與其他事務(wù)隔離的程度稱(chēng)為隔離級(jí)別。SQL標(biāo)準(zhǔn)中規(guī)定了多種事務(wù)隔離級(jí)別,不同隔離級(jí)別對(duì)應(yīng)不同的干擾程度,隔離級(jí)別越高,數(shù)據(jù)一致性就越好,但并發(fā)性越弱。
隔離級(jí)別一共有四種:
讀未提交:READ UNCOMMITTED
允許Transaction01讀取Transaction02未提交的修改。
讀已提交:READ COMMITTED、
要求Transaction01只能讀取Transaction02已提交的修改。
可重復(fù)讀:REPEATABLE READ
確保Transaction01可以多次從一個(gè)字段中讀取到相同的值,即Transaction01執(zhí)行期間禁止其它
事務(wù)對(duì)這個(gè)字段進(jìn)行更新。
串行化:SERIALIZABLE
確保Transaction01可以多次從一個(gè)表中讀取到相同的行,在Transaction01執(zhí)行期間,禁止其它
事務(wù)對(duì)這個(gè)表進(jìn)行添加、更新、刪除操作??梢员苊馊魏尾l(fā)問(wèn)題,但性能十分低下。
@Transactional(isolation = Isolation.DEFAULT)//使用數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別 @Transactional(isolation = Isolation.READ_UNCOMMITTED)//讀未提交 @Transactional(isolation = Isolation.READ_COMMITTED)//讀已提交 @Transactional(isolation = Isolation.REPEATABLE_READ)//可重復(fù)讀 @Transactional(isolation = Isolation.SERIALIZABLE)//串行化
事務(wù)屬性:事務(wù)傳播行為
當(dāng)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),必須指定事務(wù)應(yīng)該如何傳播。例如:方法可能繼續(xù)在現(xiàn)有事務(wù)中運(yùn)行,也可能開(kāi)啟一個(gè)新事務(wù),并在自己的事務(wù)中運(yùn)行。
可以通過(guò)@Transactional中的propagation屬性設(shè)置事務(wù)傳播行為
修改BookServiceImpl中buyBook()上,注解@Transactional的propagation屬性
@Transactional(propagation = Propagation.REQUIRED),默認(rèn)情況,表示如果當(dāng)前線程上有已經(jīng)開(kāi)啟的事務(wù)可用,那么就在這個(gè)事務(wù)中運(yùn)行。經(jīng)過(guò)觀察,購(gòu)買(mǎi)圖書(shū)的方法buyBook()在checkout()中被調(diào)用,checkout()上有事務(wù)注解,因此在此事務(wù)中執(zhí)行。所購(gòu)買(mǎi)的兩本圖書(shū)的價(jià)格為80和50,而用戶(hù)的余額為100,因此在購(gòu)買(mǎi)第二本圖書(shū)時(shí)余額不足失敗,導(dǎo)致整個(gè)checkout()回滾,即只要有一本書(shū)買(mǎi)不了,就都買(mǎi)不了
@Transactional(propagation = Propagation.REQUIRES_NEW),表示不管當(dāng)前線程上是否有已經(jīng)開(kāi)啟的事務(wù),都要開(kāi)啟新事務(wù)。同樣的場(chǎng)景,每次購(gòu)買(mǎi)圖書(shū)都是在buyBook()的事務(wù)中執(zhí)行,因此第一本圖書(shū)購(gòu)買(mǎi)成功,事務(wù)結(jié)束,第二本圖書(shū)購(gòu)買(mǎi)失敗,只在第二次的buyBook()中回滾,購(gòu)買(mǎi)第一本圖書(shū)不受影響,即能買(mǎi)幾本就買(mǎi)幾本
到此這篇關(guān)于Spring聲明式事務(wù)配置使用詳解的文章就介紹到這了,更多相關(guān)Spring聲明式事務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springBoot server.port=-1的含義說(shuō)明
這篇文章主要介紹了springBoot server.port=-1的含義說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08基于Java驗(yàn)證jwt token代碼實(shí)例
這篇文章主要介紹了基于Java驗(yàn)證jwt token代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12Java由淺入深細(xì)數(shù)數(shù)組的操作下
數(shù)組對(duì)于每一門(mén)編程語(yǔ)言來(lái)說(shuō)都是重要的數(shù)據(jù)結(jié)構(gòu)之一,當(dāng)然不同語(yǔ)言對(duì)數(shù)組的實(shí)現(xiàn)及處理也不盡相同。Java?語(yǔ)言中提供的數(shù)組是用來(lái)存儲(chǔ)固定大小的同類(lèi)型元素2022-04-04簡(jiǎn)單講解Java設(shè)計(jì)模式編程中的單一職責(zé)原則
這篇文章主要介紹了Java設(shè)計(jì)模式編程中的單一職責(zé)原則,這在團(tuán)隊(duì)開(kāi)發(fā)編寫(xiě)接口時(shí)經(jīng)常使用這樣的約定,需要的朋友可以參考下2016-02-02java實(shí)現(xiàn)學(xué)生宿舍系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)學(xué)生宿舍系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03JAVA WSIMPORT生成WEBSERVICE客戶(hù)端401認(rèn)證過(guò)程圖解
這篇文章主要介紹了JAVA WSIMPORT生成WEBSERVICE客戶(hù)端401認(rèn)證過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10SpringBoot如何優(yōu)雅的整合Swagger Api自動(dòng)生成文檔
在多人協(xié)作的開(kāi)發(fā)過(guò)程中,API文檔不僅可以減少等待,也能保證開(kāi)發(fā)的持續(xù)進(jìn)行,這篇文章主要給大家介紹了關(guān)于SpringBoot如何優(yōu)雅的整合Swagger Api自動(dòng)生成文檔的相關(guān)資料,需要的朋友可以參考下2021-07-07JavaWeb?Servlet技術(shù)及其應(yīng)用實(shí)踐
這篇文章主要介紹了JavaWeb?Servlet技術(shù),Servlet指在服務(wù)器端執(zhí)行的一段Java代碼,可以接收用戶(hù)的請(qǐng)求和返回給用戶(hù)響應(yīng)結(jié)果,感興趣想要詳細(xì)了解可以參考下文2023-05-05