Spring事物詳解

管理兩種方式

spring支持編程式事務(wù)管理和聲明式事務(wù)管理兩種方式。

  • 編程式事務(wù)管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager中燥。對(duì)于編程式事務(wù)管理寇甸,spring推薦使用TransactionTemplate。
  • 聲明式事務(wù)管理建立在AOP之上的。其本質(zhì)是對(duì)方法前后進(jìn)行攔截拿霉,然后在目標(biāo)方法開始之前創(chuàng)建或者加入一個(gè)事務(wù)式塌,在執(zhí)行完目標(biāo)方法之后根據(jù)執(zhí)行情況提交或者回滾事務(wù)。

聲明式事務(wù)最大的優(yōu)點(diǎn)就是不需要通過編程的方式管理事務(wù)友浸,這樣就不需要在業(yè)務(wù)邏輯代碼中摻雜事務(wù)管理的代碼峰尝,只需在配置文件中做相關(guān)的事務(wù)規(guī)則聲明(或通過基于@Transactional注解的方式),便可以將事務(wù)規(guī)則應(yīng)用到業(yè)務(wù)邏輯中收恢。

顯然聲明式事務(wù)管理要優(yōu)于編程式事務(wù)管理武学,這正是spring倡導(dǎo)的非侵入式的開發(fā)方式。聲明式事務(wù)管理使業(yè)務(wù)代碼不受污染伦意,一個(gè)普通的POJO對(duì)象火窒,只要加上注解就可以獲得完全的事務(wù)支持。和編程式事務(wù)相比驮肉,聲明式事務(wù)唯一不足地方是熏矿,后者的最細(xì)粒度只能作用到方法級(jí)別,無法做到像編程式事務(wù)那樣可以作用到代碼塊級(jí)別离钝。但是即便有這樣的需求票编,也存在很多變通的方法,比如卵渴,可以將需要進(jìn)行事務(wù)管理的代碼塊獨(dú)立為方法等等慧域。

聲明式事務(wù)管理也有兩種常用的方式,一種是基于tx和aop名字空間的xml配置文件浪读,另一種就是基于@Transactional注解昔榴。顯然基于注解的方式更簡單易用,更清爽碘橘。

聲明式事務(wù)的使用技巧

1互订、@Transactional可以作用于接口、接口方法痘拆、類仰禽、類方法上,當(dāng)作用到類時(shí)错负,該類下所有public方法都將具有該類型的事務(wù)屬性坟瓢,同時(shí),也可以在方法級(jí)別使用該注解來覆蓋類級(jí)別的定義犹撒。Spring的建議是在具體的實(shí)現(xiàn)類和類方法使用@Transactional注解折联,而不是使用在接口上。因?yàn)樽⒔獠荒芾^承识颊,不能被基于接口的代理類所識(shí)別诚镰,注解失效奕坟。

2、聲明式事務(wù)管理默認(rèn)只對(duì)非檢查型異常unchecked Exception進(jìn)行回滾清笨,也就是對(duì)RuntimeException異常以及它的子類進(jìn)行回滾操作月杉。



如果需要讓checked Exception也進(jìn)行回滾,需加上@Transactional(rollbackFor=Exception.class)抠艾、

如果需要讓unchecked Exception不進(jìn)行回滾苛萎,需加上@Transactional(notRollbackFor=Exception.class)

3、在Springboot使用聲明式事務(wù)需要在Application啟動(dòng)類加入@EnableTransactionManagement注解检号,相當(dāng)于Spring的自動(dòng)掃描

四腌歉、聲明式事務(wù)的常用配置

參 數(shù) 名 稱 功 能 描 述
readOnly 該屬性用于設(shè)置當(dāng)前事務(wù)是否為只讀事務(wù),設(shè)置為true表示只讀齐苛,false則表示可讀寫翘盖,默認(rèn)值為false。例如:@Transactional(readOnly=true)
rollbackFor 該屬性用于設(shè)置需要進(jìn)行回滾的異常類數(shù)組凹蜂,當(dāng)方法中拋出指定異常數(shù)組中的異常時(shí)馍驯,則進(jìn)行事務(wù)回滾。例如:指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)指定多個(gè)異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
rollbackForClassName 該屬性用于設(shè)置需要進(jìn)行回滾的異常類名稱數(shù)組玛痊,當(dāng)方法中拋出指定異常名稱數(shù)組中的異常時(shí)汰瘫,則進(jìn)行事務(wù)回滾。例如:指定單一異常類名稱@Transactional(rollbackForClassName=”RuntimeException”)指定多個(gè)異常類名稱:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})
noRollbackFor 該屬性用于設(shè)置不需要進(jìn)行回滾的異常類數(shù)組卿啡,當(dāng)方法中拋出指定異常數(shù)組中的異常時(shí)吟吝,不進(jìn)行事務(wù)回滾。例如:指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class)指定多個(gè)異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName 該屬性用于設(shè)置不需要進(jìn)行回滾的異常類名稱數(shù)組颈娜,當(dāng)方法中拋出指定異常名稱數(shù)組中的異常時(shí),不進(jìn)行事務(wù)回滾浙宜。例如:指定單一異常類名稱:@Transactional(noRollbackForClassName=”RuntimeException”)指定多個(gè)異常類名稱:@Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})
propagation 該屬性用于設(shè)置事務(wù)的傳播行為官辽。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation 該屬性用于設(shè)置底層數(shù)據(jù)庫的事務(wù)隔離級(jí)別,事務(wù)隔離級(jí)別用于處理多事務(wù)并發(fā)的情況粟瞬,通常使用數(shù)據(jù)庫的默認(rèn)隔離級(jí)別即可同仆,基本不需要進(jìn)行設(shè)置
timeout 該屬性用于設(shè)置事務(wù)的超時(shí)秒數(shù),默認(rèn)值為-1表示永不超時(shí)

spring事務(wù)特性

spring所有的事務(wù)管理策略類都繼承自org.springframework.transaction.PlatformTransactionManager接口其中TransactionDefinition接口定義以下特性:

事務(wù)隔離級(jí)別

隔離級(jí)別是指若干個(gè)并發(fā)的事務(wù)之間的隔離程度裙品。TransactionDefinition 接口中定義了五個(gè)表示隔離級(jí)別的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:這是默認(rèn)值俗批,表示使用底層數(shù)據(jù)庫的默認(rèn)隔離級(jí)別。對(duì)大部分?jǐn)?shù)據(jù)庫而言市怎,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED岁忘。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級(jí)別表示一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)修改但還沒有提交的數(shù)據(jù)。該級(jí)別不能防止臟讀区匠,不可重復(fù)讀和幻讀干像,因此很少使用該隔離級(jí)別帅腌。比如PostgreSQL實(shí)際上并沒有此級(jí)別。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級(jí)別表示一個(gè)事務(wù)只能讀取另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)麻汰。該級(jí)別可以防止臟讀速客,這也是大多數(shù)情況下的推薦值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級(jí)別表示一個(gè)事務(wù)在整個(gè)過程中可以多次重復(fù)執(zhí)行某個(gè)查詢五鲫,并且每次返回的記錄都相同溺职。該級(jí)別可以防止臟讀和不可重復(fù)讀。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務(wù)依次逐個(gè)執(zhí)行位喂,這樣事務(wù)之間就完全不可能產(chǎn)生干擾浪耘,也就是說,該級(jí)別可以防止臟讀忆某、不可重復(fù)讀以及幻讀点待。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別弃舒。

事務(wù)傳播行為
所謂事務(wù)的傳播行為是指癞埠,如果在開始當(dāng)前事務(wù)之前,一個(gè)事務(wù)上下文已經(jīng)存在聋呢,此時(shí)有若干選項(xiàng)可以指定一個(gè)事務(wù)性方法的執(zhí)行行為苗踪。在TransactionDefinition定義中包括了如下幾個(gè)表示傳播行為的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù)削锰;如果當(dāng)前沒有事務(wù)通铲,則創(chuàng)建一個(gè)新的事務(wù)。這是默認(rèn)值器贩。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個(gè)新的事務(wù)颅夺,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起蛹稍。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果當(dāng)前存在事務(wù)吧黄,則加入該事務(wù);如果當(dāng)前沒有事務(wù)唆姐,則以非事務(wù)的方式繼續(xù)運(yùn)行拗慨。(注意這里,不要采坑
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務(wù)方式運(yùn)行奉芦,如果當(dāng)前存在事務(wù)赵抢,則把當(dāng)前事務(wù)掛起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事務(wù)方式運(yùn)行声功,如果當(dāng)前存在事務(wù)烦却,則拋出異常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果當(dāng)前存在事務(wù)减噪,則加入該事務(wù)短绸;如果當(dāng)前沒有事務(wù)车吹,則拋出異常。
  • TransactionDefinition.PROPAGATION_NESTED:如果當(dāng)前存在事務(wù)醋闭,則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行窄驹;如果當(dāng)前沒有事務(wù),則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED证逻。

事務(wù)超時(shí)
所謂事務(wù)超時(shí)乐埠,就是指一個(gè)事務(wù)所允許執(zhí)行的最長時(shí)間,如果超過該時(shí)間限制但事務(wù)還沒有完成囚企,則自動(dòng)回滾事務(wù)丈咐。在 TransactionDefinition 中以int 的值來表示超時(shí)時(shí)間,其單位是秒龙宏。

默認(rèn)設(shè)置為底層事務(wù)系統(tǒng)的超時(shí)值棵逊,如果底層數(shù)據(jù)庫事務(wù)系統(tǒng)沒有設(shè)置超時(shí)值,那么就是none银酗,沒有超時(shí)限制辆影。

事務(wù)只讀屬性
只讀事務(wù)用于客戶代碼只讀但不修改數(shù)據(jù)的情形,只讀事務(wù)用于特定情景下的優(yōu)化黍特,比如使用Hibernate的時(shí)候蛙讥。

“只讀事務(wù)”并不是一個(gè)強(qiáng)制選項(xiàng),它只是一個(gè)“暗示”灭衷,提示數(shù)據(jù)庫驅(qū)動(dòng)程序和數(shù)據(jù)庫系統(tǒng)次慢,這個(gè)事務(wù)并不包含更改數(shù)據(jù)的操作,那么JDBC驅(qū)動(dòng)程序和數(shù)據(jù)庫就有可能根據(jù)這種情況對(duì)該事務(wù)進(jìn)行一些特定的優(yōu)化翔曲,比方說不安排相應(yīng)的數(shù)據(jù)庫鎖迫像,以減輕事務(wù)對(duì)數(shù)據(jù)庫的壓力,畢竟事務(wù)也是要消耗數(shù)據(jù)庫的資源的瞳遍。

但是你非要在“只讀事務(wù)”里面修改數(shù)據(jù)侵蒙,也并非不可以,只不過對(duì)于數(shù)據(jù)一致性的保護(hù)不像“讀寫事務(wù)”那樣保險(xiǎn)而已傅蹂。

因此,“只讀事務(wù)”僅僅是一個(gè)性能優(yōu)化的推薦配置而已算凿,并非強(qiáng)制你要這樣做不可份蝴。

spring事務(wù)回滾規(guī)則

指示spring事務(wù)管理器回滾一個(gè)事務(wù)的推薦方法是在當(dāng)前事務(wù)的上下文內(nèi)拋出異常。spring事務(wù)管理器會(huì)捕捉任何未處理的異常氓轰,然后依據(jù)規(guī)則決定是否回滾拋出異常的事務(wù)婚夫。默認(rèn)配置下,spring只有在拋出的異常為運(yùn)行時(shí)unchecked異常時(shí)才回滾該事務(wù)署鸡,也就是拋出的異常為RuntimeException的子類(Errors也會(huì)導(dǎo)致事務(wù)回滾)案糙,而拋出checked異常則不會(huì)導(dǎo)致事務(wù)回滾限嫌。可以明確的配置在拋出那些異常時(shí)回滾事務(wù)时捌,包括checked異常怒医。也可以明確定義那些異常拋出時(shí)不回滾事務(wù)。還可以編程性的通過setRollbackOnly()方法來指示一個(gè)事務(wù)必須回滾奢讨,在調(diào)用完setRollbackOnly()后你所能執(zhí)行的唯一操作就是回滾稚叹。

示例:基于注解的聲明式事務(wù)管理配置@Transactional

spring.xml

<!-- mybatis config -->  
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
    <property name="dataSource" ref="dataSource" />  
    <property name="configLocation">  
        <value>classpath:mybatis-config.xml</value>  
    </property>  
</bean>  

<!-- mybatis mappers, scanned automatically -->  
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
    <property name="basePackage">  
        <value> com.baobao.persistence.test </value>  
    </property>  
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />  
</bean>  

<!-- 配置spring的PlatformTransactionManager,名字為默認(rèn)值 -->  
<bean id="transactionManager" 
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
    <property name="dataSource" ref="dataSource" />  
</bean>  

<!-- 開啟事務(wù)控制的注解支持 -->  
<tx:annotation-driven transaction-manager="transactionManager"/>

添加tx名字空間

xmlns:aop="http://www.springframework.org/schema/aop" 
xmlns:tx="http://www.springframework.org/schema/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"

MyBatis自動(dòng)參與到spring事務(wù)管理中拿诸,無需額外配置扒袖,只要org.mybatis.spring.SqlSessionFactoryBean引用的數(shù)據(jù)源與DataSourceTransactionManager引用的數(shù)據(jù)源一致即可,否則事務(wù)管理會(huì)不起作用亩码。

@Transactional注解

使用@Transactional時(shí)季率,可以指定如下屬性:

a、isolation:用于指定事務(wù)的隔離級(jí)別描沟。默認(rèn)為底層事務(wù)的隔離級(jí)別飒泻。
b、noRollbackFor:指定遇到指定異常時(shí)強(qiáng)制不回滾事務(wù)啊掏。
c蠢络、noRollbackForClassName:指定遇到指定多個(gè)異常時(shí)強(qiáng)制不回滾事務(wù)。該屬性可以指定多個(gè)異常類名迟蜜。
d刹孔、propagation:指定事務(wù)的傳播屬性。
e娜睛、readOnly:指定事務(wù)是否只讀髓霞。
f、rollbackFor:指定遇到指定異常時(shí)強(qiáng)制回滾事務(wù)畦戒。
g方库、rollbackForClassName:指定遇到指定多個(gè)異常時(shí)強(qiáng)制回滾事務(wù)。該屬性可以指定多個(gè)異常類名。
h抵乓、timeout:指定事務(wù)的超時(shí)時(shí)長摆寄。**

事務(wù)可回滾要注意點(diǎn)*

1、被注解的必須是public

@Transactional 可以作用于接口邀层、接口方法、類以及類方法上遂庄。當(dāng)作用于類上時(shí)寥院,該類的所有 public方法將都具有該類型的事務(wù)屬性,同時(shí)涛目,我們也可以在方法級(jí)別使用該標(biāo)注來覆蓋類級(jí)別的定義秸谢。

雖然 @Transactional 注解可以作用于接口凛澎、接口方法、類以及類方法上估蹄,但是 Spring 建議不要在接口或者接口方法上使用該注解塑煎,因?yàn)檫@只有在使用基于接口的代理時(shí)它才會(huì)生效。另外元媚, @Transactional 注解應(yīng)該只被應(yīng)用到 public 方法上轧叽,這是由 Spring AOP 的本質(zhì)決定的。如果你在 protected刊棕、private 或者默認(rèn)可見性的方法上使用 @Transactional 注解炭晒,這將被忽略,也不會(huì)拋出任何異常甥角。

poxy-target-class="true"表示使用CGLib動(dòng)態(tài)代理技術(shù)織入增強(qiáng)网严。不過即使proxy-target-class設(shè)置為false,如果目標(biāo)類沒有聲明接口嗤无,則spring將自動(dòng)使用CGLib動(dòng)態(tài)代理震束。</pre>

2、rollbackFor和noRollbackFor

需我們指定方式來讓事務(wù)回滾 :
要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常})
如果讓unchecked例外不回滾:@Transactional(notRollbackFor=RunTimeException.class)

(關(guān)于rollbackFor配置的經(jīng)歷)

當(dāng)?shù)腡ransactional中配置rollbackFor = Exception.class時(shí)当犯,拋出RuntimeException時(shí)是會(huì)回滾的垢村。但是如果是Unchecked Exceptions則不會(huì)回滾。

于是查看Spring的Transactional的API文檔嚎卫,發(fā)現(xiàn)下面這段:

If no rules are relevant to the exception, it will be treated like DefaultTransactionAttribute (rolling back on runtime exceptions).

后面又試了下發(fā)現(xiàn)嘉栓,如果不添加rollbackFor等屬性,Spring碰到Unchecked Exceptions都會(huì)回滾拓诸,不僅是RuntimeException侵佃,也包括Error。

3奠支、來自外部的方法調(diào)用才會(huì)被AOP代理捕獲

默認(rèn)情況下馋辈,只有來自外部的方法調(diào)用才會(huì)被AOP代理捕獲,也就是倍谜,類內(nèi)部方法調(diào)用本類內(nèi)部的其他方法并不會(huì)引起事務(wù)行為迈螟,即使被調(diào)用方法使用@Transactional注解進(jìn)行修飾。(我碰到后尔崔,只有重構(gòu)代碼再引入一層service解決)

示例一:(不要rollbackFor井联,unchecked異常,可以回滾)
@Autowired 
private MyBatisDao dao;  

@Transactional  
@Override public void insert(Test test) {  
    dao.insert(test); throw new RuntimeException("test");
    //拋出unchecked異常您旁,觸發(fā)事物,回滾 
} 
示例二:(noRollbackFor使用場景)
@Transactional(noRollbackFor=RuntimeException.class)  
    @Override public void insert(Test test) {  
        dao.insert(test); 
        //拋出unchecked異常轴捎,觸發(fā)事物鹤盒,noRollbackFor=RuntimeException.class,不回滾 
        throw new RuntimeException("test");  
    } 
示例三:當(dāng)作用于類上時(shí)蚕脏,該類所有 public 方法將都具有該類型的事務(wù)屬性。
@Transactional 
public class MyBatisServiceImpl implements MyBatisService {  

    @Autowired private MyBatisDao dao;  

    @Override public void insert(Test test) {  
        dao.insert(test); //拋出unchecked異常侦锯,觸發(fā)事物驼鞭,回滾 
        throw new RuntimeException("test");  
    }
}
示例四:propagation=Propagation.NOT_SUPPORTED
@Transactional(propagation=Propagation.NOT_SUPPORTED)  
@Override public void insert(Test test) { 
 //事物傳播行為是PROPAGATION_NOT_SUPPORTED,以非事務(wù)方式運(yùn)行尺碰,不會(huì)存入數(shù)據(jù)庫
 dao.insert(test);  
} 

事物注解方式: @Transactional

當(dāng)標(biāo)于類前時(shí), 標(biāo)示類中所有方法都進(jìn)行事物處理 , 例子:

 @Transactional 
public class TestServiceBean implements TestService {
}

當(dāng)類中某些方法不需要事物時(shí):

@Transactional  
 public class TestServiceBean implements TestService { 
    private TestDao dao; 
    public void setDao(TestDao dao) { 
      this.dao = dao; 
    } 

@Transactional(propagation =Propagation.NOT_SUPPORTED)
 public List getAll() { 
    return null; 
  } 
}

事物傳播行為介紹:

  • @Transactional(propagation=Propagation.REQUIRED)
    如果有事務(wù), 那么加入事務(wù), 沒有的話新建一個(gè)(默認(rèn)情況下)

  • @Transactional(propagation=Propagation.NOT_SUPPORTED)
    容器不為這個(gè)方法開啟事務(wù)

  • @Transactional(propagation=Propagation.REQUIRES_NEW)
    不管是否存在事務(wù),都創(chuàng)建一個(gè)新的事務(wù),原來的掛起,新的執(zhí)行完畢,繼續(xù)執(zhí)行老的事務(wù)

  • @Transactional(propagation=Propagation.MANDATORY)
    必須在一個(gè)已有的事務(wù)中執(zhí)行,否則拋出異常

  • @Transactional(propagation=Propagation.NEVER)
    必須在一個(gè)沒有的事務(wù)中執(zhí)行,否則拋出異常(與Propagation.MANDATORY相反)

  • @Transactional(propagation=Propagation.SUPPORTS)
    如果其他bean調(diào)用這個(gè)方法,在其他bean中聲明事務(wù),那就用事務(wù).如果其他bean沒有聲明事務(wù),那就不用事務(wù).

事物超時(shí)設(shè)置:
 @Transactional(timeout=30) //默認(rèn)是30秒

事務(wù)隔離級(jí)別:

  • @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    讀取未提交數(shù)據(jù)(會(huì)出現(xiàn)臟讀, 不可重復(fù)讀) 基本不使用

  • @Transactional(isolation = Isolation.READ_COMMITTED)
    讀取已提交數(shù)據(jù)(會(huì)出現(xiàn)不可重復(fù)讀和幻讀)

  • @Transactional(isolation = Isolation.REPEATABLE_READ)
    可重復(fù)讀(會(huì)出現(xiàn)幻讀)

  • @Transactional(isolation = Isolation.SERIALIZABLE)
    串行化

MYSQL:默認(rèn)為REPEATABLE_READ級(jí)別
SQLSERVER:默認(rèn)為READ_COMMITTED

臟讀 : 一個(gè)事務(wù)讀取到另一事務(wù)未提交的更新數(shù)據(jù)挣棕。
不可重復(fù)讀 : 在同一事務(wù)中, 多次讀取同一數(shù)據(jù)返回的結(jié)果有所不同。后續(xù)讀取可以讀到另一事務(wù)已提交的更新數(shù)據(jù)亲桥。
可重復(fù)讀:在同一事務(wù)中多次讀取數(shù)據(jù)時(shí), 能夠保證所讀數(shù)據(jù)一樣, 也就是后續(xù)讀取不能讀到另一事務(wù)已提交的更新數(shù)據(jù)洛心。
幻讀 : 一個(gè)事務(wù)讀到另一個(gè)事務(wù)已提交的insert數(shù)據(jù)。

@Transactional注解中常用參數(shù)說明

參數(shù)名稱 功能描述
readOnly 該屬性用于設(shè)置當(dāng)前事務(wù)是否為只讀事務(wù)题篷,設(shè)置為true表示只讀词身,false則表示可讀寫,默認(rèn)值為false番枚。例如:@Transactional(readOnly=true)
rollbackFor 該屬性用于設(shè)置需要進(jìn)行回滾的異常類數(shù)組法严,當(dāng)方法中拋出指定異常數(shù)組中的異常時(shí),則進(jìn)行事務(wù)回滾葫笼。例如:指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)深啤;指定多個(gè)異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
rollbackForClassName 該屬性用于設(shè)置需要進(jìn)行回滾的異常類名稱數(shù)組,當(dāng)方法中拋出指定異常名稱數(shù)組中的異常時(shí)路星,則進(jìn)行事務(wù)回滾溯街。例如:指定單一異常類名稱@Transactional(rollbackForClassName="RuntimeException");指定多個(gè)異常類名稱:@Transactional(rollbackForClassName{"RuntimeException","Exception"})
noRollbackFor 該屬性用于設(shè)置不需要進(jìn)行回滾的異常類數(shù)組奥额,當(dāng)方法中拋出指定異常數(shù)組中的異常時(shí)苫幢,不進(jìn)行事務(wù)回滾。例如:指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class)垫挨;指定多個(gè)異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName 該屬性用于設(shè)置不需要進(jìn)行回滾的異常類名稱數(shù)組韩肝,當(dāng)方法中拋出指定異常名稱數(shù)組中的異常時(shí),不進(jìn)行事務(wù)回滾九榔。例如:指定單一異常類名稱@Transactional(noRollbackForClassName="RuntimeException")

指定多個(gè)異常類名稱:

@Transactional(noRollbackForClassName={"RuntimeException","Exception"})

參數(shù)名稱 功能描述
propagation 該屬性用于設(shè)置事務(wù)的傳播行為哀峻,具體取值可參考表6-7。例@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation 該屬性用于設(shè)置底層數(shù)據(jù)庫的事務(wù)隔離級(jí)別哲泊,事務(wù)隔離級(jí)別用于處理多事務(wù)并發(fā)的情況剩蟀,通常使用數(shù)據(jù)庫的默認(rèn)隔離級(jí)別即可,基本不需要進(jìn)行設(shè)置
timeout 該屬性用于設(shè)置事務(wù)的超時(shí)秒數(shù)切威,默認(rèn)值為-1表示永不超時(shí)

注意的幾點(diǎn):

1育特、@Transactional 只能被應(yīng)用到public方法上, 對(duì)于其它非public的方法,如果標(biāo)記了@Transactional也不會(huì)報(bào)錯(cuò),但方法沒有事務(wù)功能.

2、用 spring 事務(wù)管理器,由spring來負(fù)責(zé)數(shù)據(jù)庫的打開,提交,回滾.默認(rèn)遇到運(yùn)行期例外(throw new RuntimeException("注釋");)會(huì)回滾,即遇到不受檢查(unchecked)的例外時(shí)回滾缰冤;而遇到需要捕獲的例外(throw new Exception("注釋");)不會(huì)回滾,即遇到受檢查的例外(就是非運(yùn)行時(shí)拋出的異常犬缨,編譯器會(huì)檢查到的異常叫受檢查例外或說受檢查異常)時(shí),需我們指定方式來讓事務(wù)回滾要想所有異常都回滾棉浸。
要加上 @Transactional( rollbackFor={Exception.class,其它異常}) 怀薛。如果讓unchecked例外不回滾:@Transactional(notRollbackFor=RunTimeException.class)如下:

@Transactional(rollbackFor=Exception.class) 
//指定回滾,遇到異常Exception時(shí)回滾
public void methodName() { 
throw new Exception("注釋"); 
}

@Transactional(noRollbackFor=Exception.class)
//指定不回滾,遇到運(yùn)行期例外(throw new RuntimeException("注釋");)會(huì)回滾
public ItimDaoImpl getItemDaoImpl() { 
throw new RuntimeException("注釋"); 
}

3、@Transactional 注解應(yīng)該只被應(yīng)用到 public 可見度的方法上迷郑。 如果你在 protected枝恋、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會(huì)報(bào)錯(cuò)嗡害, 但是這個(gè)被注解的方法將不會(huì)展示已配置的事務(wù)設(shè)置焚碌。

4、@Transactional 注解可以被應(yīng)用于接口定義和接口方法就漾、類定義和類的 public 方法上呐能。然而,請(qǐng)注意僅僅 @Transactional 注解的出現(xiàn)不足于開啟事務(wù)行為抑堡,它僅僅 是一種元數(shù)據(jù)摆出,能夠被可以識(shí)別 @Transactional 注解和上述的配置適當(dāng)?shù)木哂惺聞?wù)行為的beans所使用。上面的例子中首妖,其實(shí)正是 元素的出現(xiàn) 開啟 了事務(wù)行為偎漫。

5、Spring團(tuán)隊(duì)的建議是你在具體的類(或類的方法)上使用 @Transactional 注解有缆,而不要使用在類所要實(shí)現(xiàn)的任何接口上象踊。你當(dāng)然可以在接口上使用 @Transactional 注解,但是這將只能當(dāng)你設(shè)置了基于接口的代理時(shí)它才生效棚壁。因?yàn)樽⒔馐遣荒芾^承的杯矩,這就意味著如果你正在使用基于類的代理時(shí),那么事務(wù)的設(shè)置將不能被基于類的代理所識(shí)別袖外,而且對(duì)象也將不會(huì)被事務(wù)代理所包裝(將被確認(rèn)為嚴(yán)重的)史隆。因此,請(qǐng)接受Spring團(tuán)隊(duì)的建議并且在具體的類上使用 @Transactional 注解曼验。

嵌套事務(wù)示例

  • Propagation.REQUIRED+Propagation.REQUIRES_NEW

@Service 
public class ServiceAImpl implements ServiceA {

    @Autowired private ServiceB serviceB;
    @Autowired private VcSettleMainMapper vcSettleMainMapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false) 
    public void methodA() {
        String id = IdGenerator.generatePayId("A");
        VcSettleMain vc = buildModel(id);
        vcSettleMainMapper.insertVcSettleMain(vc);
        System.out.println("ServiceAImpl VcSettleMain111:" + vc);

        serviceB.methodB();  

        VcSettleMain vc2 = buildModel(id);
        vcSettleMainMapper.insertVcSettleMain(vc2);
        System.out.println("ServiceAImpl VcSettleMain22222:" + vc2);
    } 

    private VcSettleMain buildModel(String id) {
        VcSettleMain vc = new VcSettleMain();
        vc.setBatchNo(id);
        vc.setCreateBy("dxz");
        vc.setCreateTime(LocalDateTime.now());
        vc.setTotalCount(11L);
        vc.setTotalMoney(BigDecimal.ZERO);
        vc.setState("5"); return vc;
    }

}

ServiceB

@Service public class ServiceBImpl implements ServiceB {

    @Autowired private VcSettleMainMapper vcSettleMainMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false) 
    public void methodB() {
        String id = IdGenerator.generatePayId("B");
        VcSettleMain vc = buildModel(id);
        vcSettleMainMapper.insertVcSettleMain(vc);
        System.out.println("---ServiceBImpl VcSettleMain:" + vc);
    }
}

controller

@RestController
@RequestMapping("/demo")
public class Demo1 {
    @Autowired 
    private ServiceA serviceA; 
    /** * 嵌套事務(wù)測試 */ 
    @PostMapping(value = "/test1") 
    public String methodA() throws Exception {
        serviceA.methodA(); 
        return "ok";
    }
}

結(jié)果:

image

看數(shù)據(jù)庫表記錄:

image

這種情況下, 因?yàn)?ServiceB#methodB 的事務(wù)屬性為 PROPAGATION_REQUIRES_NEW泌射,ServiceB是一個(gè)獨(dú)立的事務(wù),與外層事務(wù)沒有任何關(guān)系鬓照。如果ServiceB執(zhí)行失斎劭帷(上面示例中讓ServiceB的id為已經(jīng)存在的值),ServiceA的調(diào)用出會(huì)拋出異常豺裆,導(dǎo)致ServiceA的事務(wù)回滾拒秘。

并且, 在 ServiceB#methodB 執(zhí)行時(shí) ServiceA#methodA 的事務(wù)已經(jīng)掛起了 (關(guān)于事務(wù)掛起的內(nèi)容已經(jīng)超出了本文的討論范圍)。

  • Propagation.REQUIRED+Propagation.REQUIRED

//ServiceA //...
@Transactional(propagation = Propagation.REQUIRED, readOnly = false) 
public void methodA() { 

  //ServiceB //...
 @Transactional(propagation = Propagation.REQUIRED, readOnly = false) 
  public void methodB(String id) { 
    //...
  }
}

--“1”可插入翼抠,“2”可插入咙轩,“3”不可插入:

結(jié)果是“1”,“2”阴颖,“3”都不能插入,“1”丐膝,“2”被回滾量愧。

--“1”可插入,“2”不可插入帅矗,“3”可插入:

結(jié)果是“1”偎肃,“2”,“3”都不能插入浑此,“1”累颂,“2”被回滾。

  • Propagation.REQUIRED+無事務(wù)注解

//ServiceA //...
@Transactional(propagation = Propagation.REQUIRED, readOnly = false) 
public void methodA() { 

  //ServiceB //...
  //沒有加事務(wù)注解
  public void methodB(String id) {
     //..
  }
}

--“1”可插入凛俱,“2”可插入紊馏,“3”不可插入:

結(jié)果是“1”,“2”蒲犬,“3”都不能插入朱监,“1”,“2”被回滾原叮。

2.4赫编、內(nèi)層事務(wù)被try-catch:

try-catch + Propagation.REQUIRED+Propagation.REQUIRED

//ServiceA //...
@Transactional(propagation = Propagation.REQUIRED, readOnly = false) 
public void methodA() { 
  try {
    serviceB.methodB(id);
  } catch (Exception e) {
    System.out.println("內(nèi)層事務(wù)出錯(cuò)啦。");
  }
} 

//ServiceB //...
@Transactional(propagation = Propagation.REQUIRED, readOnly = false) 
public void methodB(String id) {
   //...
}

--“1”可插入奋隶,“2”不可插入擂送,“3”可插入:

結(jié)果是“1”,“2”唯欣,“3”都不能插入嘹吨,“1”被回滾。

事務(wù)設(shè)置為Propagation.REQUIRED時(shí)黍聂,如果內(nèi)層方法拋出Exception躺苦,外層方法中捕獲Exception但是并沒有繼續(xù)向外拋出,最后出現(xiàn)“Transaction rolled back because it has been marked as rollback-only”的錯(cuò)誤产还。外層的方法也將會(huì)回滾匹厘。

其原因是:內(nèi)層方法拋異常返回時(shí),transacation被設(shè)置為rollback-only了脐区,但是外層方法將異常消化掉愈诚,沒有繼續(xù)向外拋,那么外層方法正常結(jié)束時(shí),transaction會(huì)執(zhí)行commit操作炕柔,但是transaction已經(jīng)被設(shè)置為rollback-only了酌泰。所以,出現(xiàn)“Transaction rolled back because it has been marked as rollback-only”錯(cuò)誤匕累。

  • try-catch + Propagation.REQUIRED+Propagation.NESTED

//ServiceA //...
@Transactional(propagation = Propagation.REQUIRED, readOnly = false) 
public void methodA() { 
  try {
    serviceB.methodB(id);
  } catch (Exception e) {
    System.out.println("內(nèi)層事務(wù)出錯(cuò)啦陵刹。");
  }
}

 //ServiceB //...
@Transactional(propagation = Propagation.NESTED, readOnly = false) 
public void methodB(String id) { 
  //...
}

--“1”可插入,“2”不可插入欢嘿,“3”可插入:

結(jié)果是“1”衰琐,“3"記錄插入成功,“2”記錄插入失敗炼蹦。

說明:

當(dāng)內(nèi)層配置成 PROPAGATION_NESTED, 此時(shí)兩者之間又將如何協(xié)作呢? 從 Juergen Hoeller 的原話中我們可以找到答案, ServiceB#methodB 如果 rollback, 那么內(nèi)部事務(wù)(即 ServiceB#methodB) 將回滾到它執(zhí)行前的 SavePoint(注意, 這是本文中第一次提到它, 潛套事務(wù)中最核心的概念), 而外部事務(wù)(即 ServiceA#methodA) 可以有以下兩種處理方式:

  • 內(nèi)層失敗羡宙,外層調(diào)用其它分支。
ServiceA { 
  // 事務(wù)屬性配置為 PROPAGATION_REQUIRED 
  void methodA() { 
    try {  
      ServiceB.methodB();  
    } catch (SomeException) { 
      // 執(zhí)行其他業(yè)務(wù), 如 ServiceC.methodC(); 
   }  
}  

這種方式也是潛套事務(wù)最有價(jià)值的地方, 它起到了分支執(zhí)行的效果, 如果ServiceB.methodB 失敗, 那么執(zhí)行 ServiceC.methodC(), 而 ServiceB.methodB 已經(jīng)回滾到它執(zhí)行之前的 SavePoint, 不會(huì)產(chǎn)生臟數(shù)據(jù)(相當(dāng)于此方法從未執(zhí)行過), 該特性可用在某些特殊的業(yè)務(wù)中, PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都沒有辦法做到這一點(diǎn)掐隐。

  • 代碼不做任何修改, 那么如果內(nèi)部事務(wù)(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滾到它執(zhí)行之前的 SavePoint(在任何情況下都會(huì)如此), 外部事務(wù)(即 ServiceA#methodA) 將根據(jù)具體的配置決定自己是 commit 還是 rollback狗热。

三、嵌套事務(wù)總結(jié)

使用嵌套事務(wù)的場景有兩點(diǎn)需求:

  • 需要事務(wù)BC與事務(wù)AD一起commit虑省,即:作為事務(wù)AD的子事務(wù)匿刮,事務(wù)BC只有在事務(wù)AD成功commit時(shí)(階段3成功)才commit。這個(gè)需求簡單稱之為“聯(lián)合成功”慷妙。這一點(diǎn)PROPAGATION_NESTED和PROPAGATION_REQUIRED可以做到僻焚。
  • 需要事務(wù)BC的rollback不(無條件的)影響事務(wù)AD的commit。這個(gè)需求簡單稱之為“隔離失敗”膝擂。這一點(diǎn)PROPAGATION_NESTED和PROPAGATION_REQUIRES_NEW可以做到虑啤。

分解下,可知PROPAGATION_NESTED的特殊性有:

1架馋、使用PROPAGATION_REQUIRED滿足需求1狞山,但子事務(wù)BC的rollback會(huì)無條件地使父事務(wù)AD也rollback,不能滿足需求2叉寂。即使對(duì)子事務(wù)進(jìn)行了try-catch萍启,父事務(wù)AD也不能commit。示例見2.4.1屏鳍、trycatch+Propagation.REQUIRED+Propagation.REQUIRED

2勘纯、使用PROPAGATION_REQUIRES_NEW滿足需求2,但子事務(wù)(這時(shí)不應(yīng)該稱之為子事務(wù))BC是完全新的事務(wù)上下文钓瞭,父事務(wù)(這時(shí)也不應(yīng)該稱之為父事務(wù))AD的成功與否完全不影響B(tài)C的提交驳遵,不能滿足需求1。

同時(shí)滿足上述兩條需求就要用到PROPAGATION_NESTED了山涡。PROPAGATION_NESTED在事務(wù)AD執(zhí)行到B點(diǎn)時(shí)堤结,設(shè)置了savePoint(關(guān)鍵)唆迁。

當(dāng)BC事務(wù)成功commit時(shí),PROPAGATION_NESTED的行為與PROPAGATION_REQUIRED一樣竞穷。只有當(dāng)事務(wù)AD在D點(diǎn)成功commit時(shí)唐责,事務(wù)BC才真正commit,如果階段3執(zhí)行異常瘾带,導(dǎo)致事務(wù)AD rollback鼠哥,事務(wù)BC也將一起rollback ,從而滿足了“聯(lián)合成功”看政。

當(dāng)階段2執(zhí)行異常肴盏,導(dǎo)致BC事務(wù)rollback時(shí),因?yàn)樵O(shè)置了savePoint帽衙,AD事務(wù)可以選擇與BC一起rollback或繼續(xù)階段3的執(zhí)行并保留階段1的執(zhí)行結(jié)果,從而滿足了“隔離失敗”贞绵。

當(dāng)然厉萝,要明確一點(diǎn),事務(wù)傳播策略的定義是在聲明或事務(wù)管理范圍內(nèi)的(首先是在EJB CMT規(guī)范中定義榨崩,Spring事務(wù)框架補(bǔ)充了PROPAGATION_NESTED)谴垫,編程式的事務(wù)管理不存在事務(wù)傳播的問題。

四母蛛、PROPAGATION_NESTED的必要條件

上面大致講述了潛套事務(wù)的使用場景, 下面我們來看如何在 spring 中使用 PROPAGATION_NESTED, 首先來看 AbstractPlatformTransactionManager

// Create a TransactionStatus for an existing transaction. 
private TransactionStatus handleExistingTransaction(  
        TransactionDefinition definition, Object transaction, 
        boolean debugEnabled) throws TransactionException {  
   
  ... 省略

 if (definition.getPropagationBehavior() == 
    TransactionDefinition.PROPAGATION_NESTED) {
   if (!isNestedTransactionAllowed()) { 
      throw new NestedTransactionNotSupportedException( "Transaction 
          manager does not allow nested transactions by default - " +  
          "specify 'nestedTransactionAllowed' property with value 'true'");  
   } 

    if (debugEnabled) {  
      logger.debug("Creating nested transaction with name 
        [" + definition.getName() + "]");  
     }

     if (useSavepointForNestedTransaction()) {
         // Create savepoint within existing Spring-managed transaction, 
         // through the SavepointManager API implemented by TransactionStatus. 
         // Usually uses JDBC 3.0 savepoints. 
        // Never activates Spring synchronization. 

        DefaultTransactionStatus status = newTransactionStatus(definition, 
          transaction, false, false, debugEnabled, null);  

        status.createAndHoldSavepoint(); 
      return status;  
    } else { 
      // Nested transaction through nested begin and commit/rollback calls. 
      // Usually only for JTA: Spring synchronization might get activated here 
      // in case of a pre-existing JTA transaction. 
      doBegin(transaction, definition); 

      boolean newSynchronization = (this.transactionSynchronization != 
        SYNCHRONIZATION_NEVER); 

      return newTransactionStatus(definition, transaction, true, 
        newSynchronization, debugEnabled, null);  
      }  
   }  
} 
  • 我們要設(shè)置 transactionManager 的 nestedTransactionAllowed 屬性為 true, 注意, 此屬性默認(rèn)為 false!!! **
    再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法
// Create a savepoint and hold it for the transaction. 
// @throws org.springframework.transaction.NestedTransactionNotSupportedException 
// if the underlying transaction does not support savepoints 
public void createAndHoldSavepoint() throws TransactionException {  
    setSavepoint(getSavepointManager().createSavepoint());  
} 

可以看到 Savepoint 是 SavepointManager.createSavepoint 實(shí)現(xiàn)的, 再看 SavepointManager 的層次結(jié)構(gòu), 發(fā)現(xiàn)其 Template 實(shí)現(xiàn)是JdbcTransactionObjectSupport, 常用的 DatasourceTransactionManager, HibernateTransactionManager 中的 TransactonObject 都是它的子類 :
JdbcTransactionObjectSupport 告訴我們必須要滿足兩個(gè)條件才能 createSavepoint :

  • java.sql.Savepoint 必須存在, 即 jdk 版本要 1.4+ **

  • Connection.getMetaData().supportsSavepoints() 必須為 true, 即 jdbc drive 必須支持 JDBC 3.0 **
    確保以上條件都滿足后, 你就可以嘗試使用 PROPAGATION_NESTED 了翩剪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市彩郊,隨后出現(xiàn)的幾起案子前弯,更是在濱河造成了極大的恐慌,老刑警劉巖秫逝,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恕出,死亡現(xiàn)場離奇詭異,居然都是意外死亡违帆,警方通過查閱死者的電腦和手機(jī)浙巫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刷后,“玉大人的畴,你說我怎么就攤上這事〕⒌ǎ” “怎么了丧裁?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長班巩。 經(jīng)常有香客問我渣慕,道長嘶炭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任眨猎,我火速辦了婚禮,結(jié)果婚禮上强经,老公的妹妹穿的比我還像新娘。我一直安慰自己玲躯,他們只是感情好据德,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著跷车,像睡著了一般棘利。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上朽缴,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天善玫,我揣著相機(jī)與錄音,去河邊找鬼密强。 笑死茅郎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的誓斥。 我是一名探鬼主播只洒,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼劳坑!你這毒婦竟也來了毕谴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤距芬,失蹤者是張志新(化名)和其女友劉穎涝开,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體框仔,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舀武,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了离斩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片银舱。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瘪匿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寻馏,到底是詐尸還是另有隱情棋弥,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布诚欠,位于F島的核電站顽染,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏轰绵。R本人自食惡果不足惜粉寞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望左腔。 院中可真熱鬧唧垦,春花似錦、人聲如沸液样。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蓄愁。三九已至,卻和暖如春狞悲,著一層夾襖步出監(jiān)牢的瞬間撮抓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國打工摇锋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丹拯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓荸恕,卻偏偏與公主長得像乖酬,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子融求,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 事務(wù)的嵌套概念 所謂事務(wù)的嵌套就是兩個(gè)事務(wù)方法之間相互調(diào)用咬像。spring事務(wù)開啟 ,或者是基于接口的或者是基于類的...
    jackcooper閱讀 1,420評(píng)論 0 10
  • 事務(wù)接口定義 在Spring中生宛,事務(wù)是通過TransactionDefinition接口定義的县昂。其中定義了訪問事務(wù)...
    追夢人Plus閱讀 1,162評(píng)論 0 12
  • 概念 為了保證對(duì)于數(shù)據(jù)庫的每一步操作都是可靠的,即使出現(xiàn)了異常情況陷舅,也不至于破壞數(shù)據(jù)的完整性倒彰。 事務(wù)可以在不同的S...
    意大利大炮閱讀 320評(píng)論 0 1
  • 1 事務(wù) 1.1 事務(wù)管理方式 spring支持編程式事務(wù)管理和聲明式事務(wù)管理兩種方式。 編程式事務(wù)管理使用Tra...
    鑫奕航閱讀 2,801評(píng)論 0 1
  • ??豬閱讀 124評(píng)論 0 3