一、spring事務管理架構
image
1. PlatformTranscationManager
- 通過這個接口,spring為各個平臺如JDBC、Hibernate、JtA、JPA等提供了統(tǒng)一的事務管理API,所以spring本身并不直接管理事務,而是將事務的管理托管給各個持久化平臺的實現(xiàn)。
2. TranscationStatus
- 描述事務狀態(tài)
- isNewTransaction
- hasSavePoint
- isRollBackOnly
- isConpleted
3. TranscationManager
- 事務管理器
- 在這里順便提一下,問什么要使用spring的事務管理,如果不使用的話,即手動進行事務管理,那么如果底層用的是JDBC事務,就需要在services層(處理事務的層)加入很多JDBC依賴的事務處理代碼(Connection、Datasource),如果是Hibernate,就需要加入很多Session相關的代碼,所以如果需要在這幾種平臺之間互換的話,需要改很多業(yè)務層的代碼,而Spring的事務管理就相當于為這些不同平臺的事務管理接口提供了一個統(tǒng)一的API,這樣即使平臺不同,但是在業(yè)務層使用的代碼都是相同的(都是使用PlatFormTransactionManager這個接口提供的API),這樣一來,如果需要更改平臺,就不需要更改業(yè)務層的代碼。
- spring事務管理就是一個典型的策略模式,通過為其注入不同的事務管理器的實現(xiàn),進而支持不同持久化方案的事務管理**
- JDBC:JDBC是通過java.sql.connection來管理事務,后者是由datasource獲得的。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>- HIbernate:Hibernate的事務是通過org.hibernate.Transcation來實現(xiàn)的,后者是從Hibernate Session 獲得的。
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
4. TranscationDefinition
- 定義事務屬性
image
5. 事務傳播行為
- 當在一個事務方法中調用另一個事務方法時,要指定事務應該如何傳播,比如是新建一個事務還是在當前事務繼續(xù)執(zhí)行。
| behavior | throw exception | new transaction | suspend curent transaction |
|---|---|---|---|
| not_suport | no | no | yes |
| supports | no | no | no |
| required | no | null -> yes | no |
| require_new | no | yes | yes |
| nested | no | yes | yes |
| never | yes | no | no |
| mandatory | yes | no | no |
嵌套事務的特點是:外層事務失敗的時候回回滾內層事務,而內層事務的異常不會引起外層事務的回滾,嵌套事務是外部事務的一部分,啟動嵌套事務的時候回保存一個savepoint,如果失敗回回滾到這個保存點,嵌套事務只有在外層事務執(zhí)行成功的時候才會commit。
而require_new不會回滾內層事務,它是啟動一個新的,不依賴于當前事務的環(huán)境,
-
隔離規(guī)則
- 定義一個事務可能受其他并發(fā)事務的影響程度
- 并發(fā)事務引起的問題
| ** | Definition |
|---|---|
| 臟讀 | A事務讀取了B事務修改了但還沒有提交的數(shù)據(jù),但是B事務因為失敗而回滾了,這個時候A事務讀取的數(shù)據(jù)是無效的 |
| 不可重復讀 | A事務進行了同樣的兩次查詢,但是獲得的數(shù)據(jù)不一樣,一般是因為兩次查詢中間有B事務對數(shù)據(jù)進行了更新 |
| 幻讀 | 類似于不可重復讀,但是重點是B事務對原數(shù)據(jù)進行了增刪而不是修改,不可重復讀側重于修改 |
| 隔離級別 | 含義 | 作用(阻止了以上那個問題的發(fā)生) |
|---|---|---|
| default | 使用數(shù)據(jù)庫默認的隔離級別 | -- |
| read_uncommitted | 允許讀取未提交的數(shù)據(jù) | -- |
| read_committed | 只允許讀取已提交的數(shù)據(jù) | 臟讀 |
| repetable_read | 多次讀取同一字段得到同樣的數(shù)據(jù),除非是本身修改 | 臟讀、不可重復讀 |
| serializable | 串行化執(zhí)行事務操作 | 臟讀、不可重復讀、幻讀 |
6. 回滾規(guī)則
- 定義了事務在遇到哪些異常才會回滾,
7. 是否只讀
- 事務設置為只讀之后有利于數(shù)據(jù)庫對該操作進行相關的優(yōu)化
8. 事務超時
- 事務一旦超時就會自動回
二 、 Spring編程式事務
1. 使用TransactionTemplate
image
TranscationTemplate 類圖
- 由上可知,TransactionDefinition是用來定義事務的屬性的,所以TransactionTemplate也就具備了配置事務的能力,另外還需要為其指定TransactionManager,用來實現(xiàn)底層事務的管理。
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>${jdbc.mgr.url}</value>
</property>
<property name="username">
<value>${jdbc.mgr.user}</value>
</property>
<property name="password">
<value>${jdbc.mgr.password}</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan">
<list>
<!-- 可以加多個包 -->
<value>com.cuilei01.mgr.utils</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置transactionTemplate -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
<!--定義事務隔離級別,-1表示使用數(shù)據(jù)庫默認級別-->
<property name="readOnly" value="false"></property>
<property name="isolationLevelName" value="ISOLATION_DEFAULT"></property>
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"></property>
</bean>
- 然后就可以在代碼中注入bean并使用
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class TransactionTest {
private final Logger logger = LoggerFactory.getLogger(TransactionTest.class);
@Resource
private TransactionTemplate transactionTemplate;
@Test
public void testProgrammaticTransaction() {
logger.info("Begin test programmatic transaction!########################");
// 第一個事務
Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
logger.info("Do in transaction with a return value!#####################################");
// 在事務中執(zhí)行, 有返回值
return 1;
}
});
logger.info("result:{}", result);
// 第二個事務
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
logger.info("Do in transaction without a return value!#####################################");
// 在事務中執(zhí)行,沒有返回值
}
});
}
-
- 使用PlatformTranscationManager
- 其實現(xiàn)步驟基本和TransactionTemplate相同,spring推薦使用后者
三、Spring聲明式事務管理
- 聲明式事務管理式建立在AOP基礎上的,其本質是在方法執(zhí)行的前后進行攔截,然后再目標方法開始之前開啟一個事務,或者加入一個已經(jīng)存在的事務, 在執(zhí)行完方法之后根據(jù)情況是否提交或者回滾,。聲明式事務管理的優(yōu)點就是不需要使用代碼管理事務,所以也就不需要再業(yè)務層加入事務管理相關的代碼,將所有事務管理相關的代碼都放到配置文件中。此外,聲明式事務管理還符合spring的“非侵入式”的編程原則,使得業(yè)務代碼不受污染,一個普通的POJO只需要簡單的加上注解就可以獲得事務管理的支持,相比于編程式事務的缺點是粒度比后者大,只到方法級別,編程式事務可以管理到代碼塊級別。
- 以mybatis為例,基于上面一步已經(jīng)配置好數(shù)據(jù)源和對應平臺的事務管理器.
- 開啟注解支持
<!-- 開啟事務控制的注解支持 --> <tx:annotation-driven transaction-manager="transactionManager"/></span></span>- 添加tx名字空間
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"- 在需要使用事務的方法或者類上加上注解,并設置相關事務屬性
@Autowired private MyBatisDao dao; @Transactional @Override public void insert(Test test) { dao.insert(test); throw new RuntimeException("test");//拋出unchecked異常,觸發(fā)事物,回滾 } - 相關事務屬性如下:
| 屬性 | 類型 | 描述 |
|---|---|---|
| value | String | 可選的限定描述符,指定使用的事務管理器 |
| propagation | enum: Propagation | 可選的事務傳播行為設置 |
| isolation | enum: Isolation | 可選的事務隔離級別設置 |
| readOnly | boolean | 讀寫或只讀事務,默認讀寫 |
| timeout | int (in seconds granularity) | 事務超時時間設置 |
| rollbackFor | Class對象數(shù)組,必須繼承自Throwable | 導致事務回滾的異常類數(shù)組 |
| rollbackForClassName | 類名數(shù)組,必須繼承自Throwable 導致事務回滾的異常類名字數(shù)組 | |
| noRollbackFor | Class對象數(shù)組,必須繼承自Throwable 不會導致事務回滾的異常類數(shù)組 |
noRollbackForClassName 類名數(shù)組,必須繼承自Throwable 不會導致事務回滾的異常類名字數(shù)組