Spring中的事務(wù)操作、注解及XML配置詳解
事務(wù)
事務(wù)全稱(chēng)叫數(shù)據(jù)庫(kù)事務(wù),是數(shù)據(jù)庫(kù)并發(fā)控制時(shí)的基本單位,它是一個(gè)操作集合,這些操作要么不執(zhí)行,要么都執(zhí)行,不可分割。例如我們的轉(zhuǎn)賬這個(gè)業(yè)務(wù),就需要進(jìn)行數(shù)據(jù)庫(kù)事務(wù)的處理。
轉(zhuǎn)賬中至少會(huì)涉及到兩條 SQL 語(yǔ)句:
update Acoount set balance = balance - money where id = 'A'; update Acoount set balance = balance + money where id = 'B'
上面這兩條 SQL 就可以要看成是一個(gè)事務(wù),必須都執(zhí)行,或都不執(zhí)行。如何保證呢,一般這樣表示:
# 開(kāi)啟事務(wù) begin transaction update Account set balance = balance - money where id = 'A'; update Account set balance = balance + money where id = 'B' # 提交事務(wù) commit transaction Exception # 回滾事務(wù) rollback transaction
事務(wù)的特性(筆試的時(shí)候會(huì)有)
Atomic(原子性):事務(wù)中包含的操作被看做一個(gè)邏輯單元,這個(gè)邏輯單元中的操作要么全部成功,要么全部失敗。
Consistency(一致性):只有合法的數(shù)據(jù)可以被寫(xiě)入數(shù)據(jù)庫(kù),否則事務(wù)應(yīng)該將其回滾到最初狀態(tài)。在轉(zhuǎn)賬的時(shí)候不會(huì)出現(xiàn)一當(dāng)少錢(qián)了,另一方?jīng)]有增加的情況。
Isolation(隔離性):事務(wù)允許多個(gè)用戶(hù)對(duì)同一個(gè)數(shù)據(jù)進(jìn)行并發(fā)訪(fǎng)問(wèn),而不破壞數(shù)據(jù)的正確性和完整性。同時(shí),并行事務(wù)的修改必須與其他并行事務(wù)的修改相互獨(dú)立。
Durability(持久性):事務(wù)完成之后,它對(duì)于系統(tǒng)的影響是永久的,該修改即使出現(xiàn)系統(tǒng)故障也將一直保留,真實(shí)的修改了數(shù)據(jù)庫(kù)。
以上 4 個(gè)屬性常被簡(jiǎn)稱(chēng)為 acid(酸的)。
事務(wù)并發(fā)的問(wèn)題
臟讀:事務(wù)二讀取到事務(wù)一中已經(jīng)更新但是還沒(méi)有提交的數(shù)據(jù),這就是臟讀。
不可重復(fù)讀:一個(gè)事務(wù)兩次讀取同一個(gè)行數(shù)據(jù)結(jié)果不同,因?yàn)橛衅渌氖聞?wù)對(duì)數(shù)據(jù)進(jìn)行了更新。此時(shí)的數(shù)據(jù)即為不可重復(fù)讀數(shù)據(jù)。
幻讀:同一事務(wù)執(zhí)行兩次查詢(xún),結(jié)果不一致,因?yàn)橹虚g有其它的事務(wù)對(duì)數(shù)據(jù)進(jìn)行更改。
如何解決這些問(wèn)題呢?數(shù)據(jù)庫(kù)系統(tǒng)為事務(wù)設(shè)置了 4 種不同的隔離級(jí)別。
事務(wù)隔離級(jí)別
讀未提交(read uncommitted):最低級(jí)別,可能會(huì)導(dǎo)入臟讀。
讀已提交(read committed):可以避免臟讀,只能查詢(xún)到已經(jīng)提交的數(shù)據(jù)。且具有良好的性能,但是不能避免不可重復(fù)讀和幻讀。
可重復(fù)讀(repeatable):解決了不可重復(fù)讀,可能會(huì)出現(xiàn)幻讀。
串行化(serializable):通過(guò)加鎖,使同一時(shí)間只能執(zhí)行一個(gè)事務(wù),不出現(xiàn)上述問(wèn)題,但是可能會(huì)導(dǎo)致大量的超時(shí)現(xiàn)象和鎖競(jìng)爭(zhēng)。
另外,MySQL 中默認(rèn)的隔離級(jí)別是可重復(fù)讀。Oracle 中默認(rèn)的事務(wù)隔離級(jí)別是讀已提交。
說(shuō)完事務(wù),想想我們?cè)?jīng)為了處理事務(wù)而寫(xiě)過(guò)的那些代碼。最后在說(shuō)說(shuō) Spring 中是如何處理的,學(xué)完 Spring 再也不用擔(dān)心事務(wù)操作了。
在 JDBC 時(shí)代我們需要這樣手動(dòng)的處理事務(wù)。
// 獲取連接 conn conn.setAutoCommit(false); 設(shè)置提交方式為手工提交 // 業(yè)務(wù)代碼 // 減錢(qián) // 加錢(qián) conn.commit(); 提交事務(wù) // 出現(xiàn)異常 conn.rollback(); 回滾事務(wù)
我們說(shuō)處理事務(wù)那是處理數(shù)據(jù)庫(kù)事務(wù),所以肯定要先有數(shù)據(jù)庫(kù)連接才能說(shuō)事務(wù)的事,而在 Java 中我們連接數(shù)據(jù)庫(kù)無(wú)非就是 JDBC,或是對(duì) JDBC 進(jìn)一步的封裝,比方說(shuō) Hibernate ,Mybatis 或是 Dbutils 這些框架,所以萬(wàn)變不離其宗,就是這么回事,就是看誰(shuí)封裝的好罷了。你們有興趣可以看看它們都是如何封裝的。
Spring 中的如何管理事務(wù)呢
首先,我們知道 Spring 是一個(gè)容器,不同的框架在處理事務(wù)時(shí)用到的對(duì)象不同,原生的 JDBC 使用 Connection ,而 Mybatis 中使用 SqlSession 對(duì)象。而 Spring 為了整合這些不同的框架,定義了一個(gè) PlatformTransactionManager 接口來(lái)統(tǒng)一標(biāo)準(zhǔn),對(duì)不同的框架又有不同的實(shí)現(xiàn)類(lèi)。
在 Spring 中根據(jù) DAO 層技術(shù)的不同來(lái)選擇不同的事務(wù)處理的對(duì)象,是 JDBC 時(shí)使用 DataSourceTransactionManager,是 Hibernate 時(shí)使用 HibernateTransitionmanager 對(duì)象,核心對(duì)象就是 Transitionmanager。
在 Spring 中管理事務(wù)會(huì)涉及到這幾個(gè)屬性,事務(wù)隔離級(jí)別、是否只讀、事務(wù)的傳播行為,說(shuō)到事務(wù)的傳播行為,指的就是不同的業(yè)務(wù)方法之間相互調(diào)用時(shí),應(yīng)該如何管理事務(wù)。Spring 中一共定義了 7 種傳播行為,無(wú)腦記住使用 required ,表示支持當(dāng)前事務(wù),若是不存在事務(wù),就創(chuàng)建一個(gè)。例如在 A 調(diào)用 B 的時(shí)候,會(huì)首先使用 A 的事務(wù),若 A 沒(méi)有事務(wù),則新創(chuàng)建一個(gè),不管 B 有沒(méi)有事務(wù)。
下面就是要實(shí)際操作一下,需要有具體的業(yè)務(wù)邏輯,還是那個(gè)轉(zhuǎn)賬的例子。來(lái)看看如何使用 Spring 來(lái)管理事務(wù),有兩種常見(jiàn)的管理方式,我們一種一種的說(shuō)。
使用 XML 配置
1 首先是導(dǎo)包,Spring 涉及的包是真的多,我有一個(gè)省事的方法,可能用到的 jar 包一下子導(dǎo)入。
2 導(dǎo)入新的約束文件,不然在 XML 無(wú)法使用 tx 標(biāo)簽。
3 準(zhǔn)備目標(biāo)對(duì)象和通知并配置。
目標(biāo)對(duì)象 AccountServiceImpl
public class AccountServiceImpl implements AccountService { private AccountDAO ad; @Transactional(isolation=Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly=false) @Override public void transfer(Integer from, Integer to, Double money) { ad.decreaseMoney(from, money); //int i = 1/0; ad.increaseMoney(to, money); } public void setAd(AccountDAO ad) { this.ad = ad; } }
在 AOP 中通知即為增強(qiáng)的代碼,而在處理事務(wù)時(shí),要增強(qiáng)的代碼無(wú)非就是開(kāi)啟事務(wù),提交事務(wù)和回滾事務(wù),所以 Spring 已經(jīng)為我們封裝好了處理事務(wù)的通知,我們只需要配置一下即可。
<!-- 導(dǎo)入 properties 配置文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置連接數(shù)據(jù)庫(kù)的核心處理對(duì)象--> <bean name="transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref= "dataSource"></property> </bean> <!-- 配置通知--> <tx:advice id="txAdvise" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/> </tx:attributes> </tx:advice> <!-- 配置 AOP ,以達(dá)成自動(dòng)處理事務(wù)的要求--> <aop:config> <aop:pointcut expression="execution(* yu.transation.*ServiceImpl.*(..))" id="txPointcut"/> <aop:advisor advice-ref="txAdvise" pointcut-ref="txPointcut"/> </aop:config> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property> <property name="driverClass" value="${jdbc.driverClass}" ></property> <property name="user" value="${jdbc.user}" ></property> <property name="password" value="${jdbc.password}" ></property> </bean> <!-- 配置 DAO 層對(duì)象--> <bean name="ad" class="yu.transation.AccountDaoImpl"> <property name="dataSource" ref = "dataSource"></property> </bean> <!-- 配置 Service 層對(duì)象--> <bean name = "accountService" class = "yu.transation.AccountServiceImpl"> <property name="ad" ref = "ad"></property> </bean>
上面的配置文件中,在配置通知時(shí),我們具體到不同的方法會(huì)有不同的配置,在項(xiàng)目應(yīng)用時(shí),會(huì)使用通配符來(lái)進(jìn)行配置。下面介紹一下使用注解來(lái)處理事務(wù),看起來(lái)會(huì)比較簡(jiǎn)單。
步驟和上面有重復(fù)的部分,需要導(dǎo)包導(dǎo)入約束,接下來(lái)就是配置一下使用注解管理事務(wù)的開(kāi)關(guān)
使用注解配置
<!-- 打開(kāi)注解配置 AOP 事務(wù) --> <tx:annotation-driven/>
下面是使用注解為 Service 中的方法配置事務(wù)處理的屬性,當(dāng)然,每一個(gè)方法都寫(xiě)會(huì)比較麻煩,也可以在類(lèi)上面使用注解,若是某個(gè)方法的處理規(guī)則不一致就單獨(dú)使用注解配置一下。
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly=false) @Override public void transfer(Integer from, Integer to, Double money) {...}
回顧一下,以上主要說(shuō)了事務(wù)以及 Spring 中處理事務(wù)的方式,而這也正是 AOP 思想在 Spring 中的應(yīng)用,我們可以看到不管是前面說(shuō)的 IoC 還是 AOP 在這里都有體現(xiàn)。
注解的出現(xiàn)是為了替換配置文件,所以我就以配置文件為主,并說(shuō)一下與之對(duì)應(yīng)的注解方式。
Spring 中的配置主要在核心配置文件 applicationContext.xml 中,由不同的標(biāo)簽來(lái)表示,所以首先我們就需要導(dǎo)入各種約束,常用的約束有 bean、context、aop、tx 。
bean 標(biāo)簽是最基本的標(biāo)簽,主要用來(lái)配置各種對(duì)象。
<!-- 屬性介紹: id: 為對(duì)象命名,唯一性標(biāo)識(shí),不能重復(fù),不能使用特殊字符。 name: 和 id 的作用類(lèi)似,區(qū)別在于可是使用特殊字符,可重復(fù),但是不建議重復(fù)。 class: 指定對(duì)象的全類(lèi)名。 init-method: 對(duì)象初始化之后立即執(zhí)行的方法。 destroy-method: 對(duì)象銷(xiāo)毀之前執(zhí)行的方法。 scope: 對(duì)象的作用范圍,可以設(shè)置單例 singleton 和多例 prototype。默認(rèn)為單例 --> <bean name="userService" class="yu.service.UserServiceImpl" > <property name="" value="" ></property> <property name="" ref="" ></property> </bean>
對(duì)應(yīng)的注解有以下幾個(gè),但是想要使用注解之前要首先配置一下……
<!-- 打開(kāi)注解配置,掃描包及其子包 --> <context:component-scan base-package="yu"></context:component-scan>
使用注解的時(shí)候,我們可以使用 @Component 來(lái)表示將這個(gè)對(duì)象交由 Spring 管理,@Scope 來(lái)指定對(duì)象的作用域。之后便可以使用 @Resource 來(lái)獲取對(duì)象。
在注冊(cè)對(duì)象的時(shí)候我們可以使用 @Component ,但是若是每一個(gè)對(duì)象都是用這個(gè)注解,不能很好的分辨出對(duì)象屬于哪一層,所以 Spring 又提供了 @Controller @Service @Repository 來(lái)分別表示控制器層,Service 層和 DAO 層的對(duì)象,功能和 @Component 是一模一樣的。
同樣的在為對(duì)象賦值的時(shí)候,我們可以使用注解 @Autowired 來(lái)自動(dòng)獲取容器中的對(duì)象,可是若是有重名的情況就需要另外一個(gè)注解 @Qualifier 來(lái)具體指定叫什么名字,這樣就有點(diǎn)麻煩了,我們一般都是直接使用 @Resource 來(lái)指定對(duì)象。
@Component("user") @Scope("prototype") public class User{ private String name; @Value(value = "18") // 屬性注入,項(xiàng)目中不用。 private Integer age; //@Autowired 自動(dòng)裝配 Car 類(lèi)型變量,同一類(lèi)型 //@Qualifier("car") 指定具體的是哪一個(gè)。 @Resource(name = "car") // 指名道姓指定是哪個(gè)對(duì)象 private Car car; ... @PostConstruct public void init(){ System.out.println("init 方法"); } @PreDestroy public void destroy(){ System.out.println("destory 方法"); } }
aop 相關(guān)的配置和注解
在 Spring 中我們可以自定義通知和切面,下面只是展示了如何配置,但是在具體的業(yè)務(wù)中應(yīng)該不會(huì)出現(xiàn) 5 種通知齊上陣的現(xiàn)象。
<aop:config> <!-- 配置切點(diǎn)--> <aop:pointcut expression="execution(* yu.service.*ServiceImpl.*(..))" id="pc"/> <aop:aspect ref="myAdvice" > <!-- 指定名為before方法作為前置通知 --> <aop:before method="before" pointcut-ref="pc" /> <!-- 后置 --> <aop:after-returning method="afterReturning" pointcut-ref="pc" /> <!-- 環(huán)繞通知 --> <aop:around method="around" pointcut-ref="pc" /> <!-- 異常攔截通知 --> <aop:after-throwing method="afterException" pointcut-ref="pc"/> <!-- 后置 --> <aop:after method="after" pointcut-ref="pc"/> </aop:aspect> </aop:config>
同樣的,我們也可以使用注解來(lái)達(dá)到自定義配置的方式。同樣的套路,想用注解配置實(shí)現(xiàn) aop,需要打開(kāi)注解配置 AOP 的開(kāi)關(guān)。
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
之后就是在通知類(lèi)中進(jìn)行配置即可。
@Aspect //通知類(lèi) public class MyAdvice { // 快速配置切點(diǎn)表達(dá)式,方法直接調(diào)用即可 @Pointcut("execution(* yu.service.*ServiceImpl.*(..))") public void pc(){} //前置通知 @Before("MyAdvice.pc()") public void before(){ System.out.println("這是前置通知!!"); } //后置通知 @AfterReturning("MyAdvice.pc()") public void afterReturning(){ System.out.println("這是后置通知(如果出現(xiàn)異常不會(huì)調(diào)用)!!"); }
context 主要是和全局有關(guān)的配置
<!-- 打開(kāi)注解配置對(duì)象,掃描包及其子包 --> <context:component-scan base-package="yu"></context:component-scan> <!-- 導(dǎo)入 properties 配置文件 --> <context:property-placeholder location="classpath:db.properties"/> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property> <property name="driverClass" value="${jdbc.driverClass}" ></property> <property name="user" value="${jdbc.user}" ></property> <property name="password" value="${jdbc.password}" ></property> </bean>
tx 配置事務(wù)管理中的通知
tx 用來(lái)配置通知對(duì)象,而這個(gè)對(duì)象是由 Spring 為我們寫(xiě)好了,而事務(wù)管理依賴(lài)于數(shù)據(jù)庫(kù)連接對(duì)象,所以你能看到 transactionManager 對(duì)象依賴(lài)于 dataSource 對(duì)象。
<bean name="transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref= "dataSource"></property> </bean> <tx:advice id="txAdvise" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut expression="execution(* yu.transation.*ServiceImpl.*(..))" id="txPointcut"/> <aop:advisor advice-ref="txAdvise" pointcut-ref="txPointcut"/> </aop:config>
使用注解配置時(shí)還是需要打開(kāi)注解配置的開(kāi)關(guān)
<!-- 打開(kāi)注解配置 AOP 事務(wù) --> <tx:annotation-driven/>
在具體的業(yè)務(wù)方法上或是類(lèi)上使用注解 @Transactional 來(lái)配置事務(wù)處理的方式。
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly=false) @Override public void transfer(Integer from, Integer to, Double money) {...}
最后有一個(gè)完美的意外,那就是 import 標(biāo)簽。用于導(dǎo)入其它的配置模塊到主配置文件中。
<!-- 導(dǎo)入其它的 Spring 配置模塊 --> <import resource="yu/transation/applicationContext.xml"/>
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- SpringBoot 注解事務(wù)聲明式事務(wù)的方式
- Spring 使用注解方式進(jìn)行事務(wù)管理配置方式
- 使用SpringBoot注解方式處理事務(wù)回滾實(shí)現(xiàn)
- spring 中事務(wù)注解@Transactional與trycatch的使用
- Spring聲明式事務(wù)注解之@EnableTransactionManagement解析
- Spring事務(wù)注解@Transactional失效的八種場(chǎng)景分析
- spring boot基于注解的聲明式事務(wù)配置詳解
- Spring @Transaction 注解執(zhí)行事務(wù)的流程
- Spring實(shí)戰(zhàn)之使用注解實(shí)現(xiàn)聲明式事務(wù)操作示例
- Spring使用注解方式處理事務(wù)
相關(guān)文章
Netty分布式pipeline管道傳播事件的邏輯總結(jié)分析
這篇文章主要為大家介紹了Netty分布式pipeline管道傳播事件總結(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03使用eclipse導(dǎo)入javaWeb項(xiàng)目的圖文教程
這篇文章主要介紹了如何使用eclipse導(dǎo)入別人的javaWeb項(xiàng)目,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07SpringMVC使用@Valid注解實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證的代碼示例
在 Web 開(kāi)發(fā)中,數(shù)據(jù)驗(yàn)證是一個(gè)非常重要的環(huán)節(jié),它可以確保數(shù)據(jù)的合法性和正確性,保護(hù)系統(tǒng)不受到惡意攻擊或用戶(hù)誤操作的影響,在 SpringMVC 中,我們可以使用 @Valid 注解來(lái)實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證,所以本文就給大家介紹具體的使用方法,需要的朋友可以參考下2023-07-07Java開(kāi)發(fā)利器之Guava?Cache的使用教程
緩存技術(shù)被認(rèn)為是減輕服務(wù)器負(fù)載、降低網(wǎng)絡(luò)擁塞、增強(qiáng)Web可擴(kuò)展性的有效途徑之一。今天咱們就來(lái)聊聊Guava?Cache本地緩存,感興趣的可以了解一下2022-09-09基于SpringBoot集成測(cè)試遠(yuǎn)程連接Redis服務(wù)的教程詳解
這篇文章主要介紹了基于SpringBoot集成測(cè)試遠(yuǎn)程連接的Redis服務(wù)的相關(guān)知識(shí),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03spring cloud 阿波羅 apollo 本地開(kāi)發(fā)環(huán)境搭建過(guò)程
Apollo(阿波羅)是攜程框架部門(mén)研發(fā)的配置管理平臺(tái),能夠集中化管理應(yīng)用不同環(huán)境、不同集群的配置,配置修改后能夠?qū)崟r(shí)推送到應(yīng)用端,并且具備規(guī)范的權(quán)限、流程治理等特性2018-01-01