什么是事務(wù)
事務(wù)是一系列操作組成的工作單元,該工作單元具有不可分割性十绑,一損俱損。滿足ACID(原子性榴啸,一致性孽惰,隔離性,持久性)
事務(wù)按分布式劃分可以分為本地事務(wù)鸥印,和分布式事務(wù)
分別由JDBC事務(wù)和JTA事務(wù)與其對應(yīng)勋功。
Transaction其實在某些具體業(yè)務(wù)上,是相當(dāng)實用的利器库说。但是我在工作之前對他的認(rèn)識只是停留在概念的層面狂鞋,現(xiàn)在想想還是很有必要好好總結(jié)一下的。
事務(wù)最經(jīng)典的例子就是銀行轉(zhuǎn)賬問題潜的,A用戶轉(zhuǎn)賬5000元給B用戶骚揍,如果在不發(fā)生任何意外的情況下,那么是一點問題沒有的,但是如果這兩部操作中間出現(xiàn)了意外(例如發(fā)生了異常)信不,很有可能這500元只轉(zhuǎn)出了嘲叔,并沒有轉(zhuǎn)入。那么這個問題的根本原因是兩個操作在代碼層面來看是相互獨立的抽活,并不具備原子性導(dǎo)致的硫戈。Spring又是怎么解決這個問題的呢?
再來通過代碼層面分析一下這個問題下硕,轉(zhuǎn)出的時候丁逝,我們通過DataSource拿到一個Connection對象,當(dāng)執(zhí)行沒有異常的時候梭姓,直接提交事務(wù)霜幼,代碼并不知道還有轉(zhuǎn)入操作的存在。
所以spring針對這一點誉尖,如果在Service層的一個方法開啟了事務(wù)罪既,那么會關(guān)閉在這個方法中調(diào)用Dao方法自動提交事務(wù)的屬性,等到整個service執(zhí)行后再做提交铡恕,具體的步驟如下:
- 獲取DataSource對象
- 通過DataSource對象獲取對應(yīng)的Connection對象
- 關(guān)閉事務(wù)的自動提交機制野崇,在Connection對象中
- 把Connection對象綁定到當(dāng)前線程中
- 在Dao中通過取得當(dāng)前線程的Connection然后執(zhí)行操作
- 如果整個Service都o(jì)k則Commit驯用,否則進行rollback
事務(wù)的隔離機制
數(shù)據(jù)庫的并發(fā)的問題奖蔓,應(yīng)運而生:例如說臟讀以蕴,虛讀,第一類丟失更新祭刚,第二類丟失更新牌捷。
解決的辦法就是通過不同的隔離機制,進行隔離:
- Read Uncommited
- Read Commited
- Repeatable Read
- Serializable
Oracle 默認(rèn)使用Read Comited涡驮, Mysql默認(rèn)使用 Repeatable Read暗甥。
隔離機制越高,性能越差捉捅。
事務(wù)的傳播規(guī)則
在一個事務(wù)方法中撤防,調(diào)用了別的事務(wù),應(yīng)該按照什么規(guī)則進行傳遞棒口。
傳播規(guī)則一共分為七種:
現(xiàn)在有這樣一種情況A方法調(diào)用了B方法寄月。
- required:必須存在一個事務(wù),如果有事務(wù)无牵,則加入到該事務(wù)漾肮,如果沒有則新建。解讀:A如果有事務(wù)茎毁,B就用A的事務(wù)克懊,如果A沒有事務(wù),則B新建一個事務(wù)
- supports:如果有事務(wù),則用谭溉。沒有則不用墙懂。解讀:A如果有事務(wù),B就用A的扮念,A如果沒有垒在,B則不用事務(wù)。
- Mandatory:必須存在事務(wù)扔亥,當(dāng)前如果有事務(wù),則用谈为。沒有則直接報異常旅挤。解讀:A如果有事務(wù),B就用A的事務(wù)伞鲫,如果沒有粘茄,則直接報錯。
- required_new: 不管當(dāng)前是否存在事務(wù)秕脓,都會創(chuàng)建一個新的柒瓣,這個在平常比較多。
- not_supports: 以非事務(wù)方式執(zhí)行吠架,如果當(dāng)前存在事務(wù)芙贫,則將當(dāng)前事務(wù)掛起 解讀:A有自己的事務(wù),B不使用A的事務(wù)傍药,B不參與A事務(wù)的管理磺平。
- never:不支持事務(wù),當(dāng)前如果存在事務(wù)拐辽,則拋出異常拣挪。
- nested:寄生事務(wù)。如果內(nèi)部事務(wù)進行回滾俱诸,不會影響到外部事務(wù)菠劝,如果外部事務(wù)回滾了,內(nèi)部事務(wù)會被影響睁搭。
Spring對事務(wù)的支持
Spring的事務(wù)管理一定要在業(yè)務(wù)層上的
- PlatformTransactionManager 根據(jù)TransactionBefination提供的事務(wù)信息赶诊,進行配置。是多種事務(wù)管理器的基類园骆。Hibernate使用的是HibernateTransactionManager甫何,Mybatis/JDBC使用的是DataSourceTransactionManager。PlatformTransactionManager 一共擁有三個方法:
- getTransaction(TransactionDefination)遇伞,在當(dāng)前環(huán)境中取得一個事務(wù)辙喂,如果不存在,則新建。有點像是一種緩存機制
- commit:提交事務(wù)
- rollback:回滾事務(wù)
- TransactionDefination:封裝了事務(wù)隔離級別巍耗,超時時間等秋麸。
- TransactionStatus:封裝了事務(wù)具體運行的狀態(tài),是否是新開的事務(wù)炬太,是否已經(jīng)提交事務(wù)
Xml方式進行配置:
下方是Spring官網(wǎng)給的例子
//業(yè)務(wù)接口:
public interface FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
<!--xml文件關(guān)于事務(wù)的配置-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 首先將剛剛的業(yè)務(wù)類注入進容器中 -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- 配置數(shù)據(jù)庫連接池灸蟆,因為連接池會作為屬性注入到TransactionManager中 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<!-- 配置PlatformTransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置transaction 具體的一些配置 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- 如果是方法名以get作為開頭的,說明是查詢方法亲族,那么配置只讀操作-->
<tx:method name="get*" read-only="true"/>
<!-- 其他的增和改操作炒考,就是用默認(rèn)的即可-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 使用Aop把transactionManager作為對業(yè)務(wù)邏輯的增強操作 -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
<!-- other <bean/> definitions here -->
</beans>
在<tx:advice/>
中的一些詳細配置,官網(wǎng)也給出了相應(yīng)的一些說明霎迫,如下圖:
Attribute | Required? | Default | Description |
---|---|---|---|
name | Yes | 事務(wù)管理的方法名稱斋枢,并且支持通配符,例如 get* , handle* , on*Event , 等等). |
|
propagation | No | REQUIRED | Transaction propagation behavior. |
isolation | No | DEFAULT | 事務(wù)的隔離級別知给,當(dāng)傳遞規(guī)則為 REQUIRED or REQUIRES_NEW 才可以設(shè)置瓤帚,當(dāng)是默認(rèn)值default的時候,指的是使用數(shù)據(jù)庫隔離級別涩赢。其他四種都是Spring 通過代碼模擬出來的 |
timeout | No | -1 | 事務(wù)超時時間 (seconds)戈次,當(dāng)傳遞規(guī)則為 REQUIRED or REQUIRES_NEW 才可以設(shè)置,默認(rèn)值-1代表使用數(shù)據(jù)庫本身的值筒扒,一般情況下怯邪,不需要進行修改。 |
read-only | No | false | 一般對查詢進行設(shè)置只讀花墩,可以提升事務(wù)的效率擎颖。只應(yīng)用于 REQUIRED or REQUIRES_NEW . |
rollback-for | No | java.lang.RunTimeException | 遇到什么異常需要做事務(wù)的回滾,例如,com.foo.MyBusinessException,ServletException.
|
no-rollback-for | No | 遇到什么異常不做回滾,com.foo.MyBusinessException,ServletException.
|
Java注解方式
首先我們需要在配置類上观游,開啟對事務(wù)的支持搂捧,使用@EnableTransactionManagement
官網(wǎng)的例子:
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
@Transactional 注解可以用來放在實現(xiàn)類上,也可以放在接口上懂缕,最好是放在實現(xiàn)類上允跑。如果加在了實現(xiàn)類上,那么也就是說對這個類里的所有方法都支持開啟事務(wù)搪柑。如果有哪個類需要一些定制化的屬性聋丝,只需要在方法上再加上這個注解并且貼上定制的屬性即可。
@Transactional可以使用的屬性:
Property | Type | Description |
---|---|---|
value | String |
|
propagation |
enum : Propagation
|
|
isolation |
enum : Isolation
|
隔離級別的設(shè)置工碾,用于傳遞屬性為 REQUIRED or REQUIRES_NEW . |
timeout |
int (in seconds of granularity) |
事務(wù)超時時間用于傳遞屬性為REQUIRES_NEW . |
readOnly | boolean |
是否為只讀. 用于傳遞屬性為 REQUIRES_NEW . |
rollbackFor | Array of Class objects, which must be derived from Throwable.
|
|
rollbackForClassName | Array of class names. The classes must be derived from Throwable.
|
哪些異常類處罰會導(dǎo)致回滾(使用異常類名) |
noRollbackFor | Array of Class objects, which must be derived from Throwable.
|
哪些異常類處罰不會導(dǎo)致回滾(使用異常類) |
noRollbackForClassName | Array of String class names, which must be derived from Throwable.
|
哪些異常類處罰不會導(dǎo)致回滾(使用異常類名) |
可以看出來這些屬性與xml配置的大同小異弱睦。