什么是Spring的事務管理
??在實際開發中,操作數據庫時都會涉及到事務管理問題,為此Spring提供了專門用于事務處理的API。Spring的事務管理簡化了傳統的事務管理流程,并在一定程度上減少了開發者的工作量。
??為Spring提供事務管理的jar包spring-tx
。
注意:
要使用Spring的事務管理,請先去了解數據庫事務管理基礎知識(并發、鎖、隔離級別等等)。
ThreadLocal
??從前面的文章我們知道,Spring通過各類模板類降低了開發者使用各種數據持久化技術的難度。這些模板類都是線程安全的,也就是說,多個DAO可以復用同一個模板實例而不會發生沖突。使用模板類訪問底層數據,根據持久化技術的不同,模板類需要綁定數據連接或會話的資源。但這些資源本身是非線程安全的,也就是說它們不能在同一時刻被多個線程共享。雖然模板類通過資源獲取數據連接或會話,但資源池本身解決的是數據連接或會話的緩存問題,并非數據連接或會話的線程安全問題。
??按照傳統經驗,如果某個對象是非線程安全的,在多線程環境下,對對象的訪問必須采用synchronized
進行線程同步。但模板類并未采用線程同步機制,因為線程同步會降低并發性,影響系統性能。此外,通過代碼同步解決線程安全的挑戰性很大,可能會增加幾倍的實現難度。那么,模板類是用了什么魔法使得可以在無須線程同步的情況下就解決線程安全的難題呢?答案就是ThreadLocal
。
??ThreadLocal
在Spring中發揮著重要的作用,在管理request作用域的Bean、事務管理、任務調度、AOP等模塊都出現了它的身影。要想了解Spring事務管理的底層技術,必須明白ThreadLocal
是什么。
ThreadLocal是什么?
??ThreadLocal
,顧名思義,它不是一個線程,而是保存線程本地化對象的容器,保存線程本地化對象的容器,保存線程本地化對象的容器!!!當運行于多線程環境的某個對象使用ThreadLocal
維護變量時,ThreadLocal
為每個使用該變量的線程分配一個獨立的變量副本。所以每個線程都可以獨立地改變自己的副本,而不會影響其他線程所對應的變量副本。從線程的角度看,這個變量就像線程專用的本地變量。
??可以總結為一句話:ThreadLocal的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度。
??InheritableThreadLocal
繼承與ThreadLocal
,它會自動為子線程復制一份從父線程那里繼承而來的本地變量:在創建子線程時,子線程會接受所有可繼承的線程本地變量的初始值。當必須將本地線程變量自動傳送給所有創建的子線程時,應盡可能地使用;InheritableThreadLocal
,而非ThreadLocal
。
推薦一篇文章知乎:ThreadLocal和synchronized的區別?
ThreadLocal與synchronized比較
??它們都是為了解決多線程中相同變量的訪問沖突問題。那么,ThreadLocal
有什么優勢?
??在synchronized
同步機制中,通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序縝密地分析什么時候對變量進行讀/寫,什么時候需要鎖定某個對象、什么時候釋放對象鎖等繁雜的問題,程序設計和編寫難度相對較大。
??而ThreadLocal
從另一個角度來解決多線程的并發訪問。ThreadLocal為每個線程提供了一個獨立的變量副本,從而隔離了多個線程對訪問數據的沖突問題。因為每個線程都擁有自己的變量副本,因而也就沒有必要對該變量進行同步。ThreadLocal
提供了線程安全的對象封裝,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal
。
Spring中的ThreadLocal
??我們知道在一般情況下,只有無狀態的Bean才可以在多線程環境下共享,在Spring中, 絕大部分Bean都可以聲明為singleton作用域。就是因為Spring對一些Bean(如RequestContextHolder、 TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態采用 ThreadLocal進行處理,讓它們也成為線程安全的狀態,因為有狀態的Bean就可以在多線程中共享了。
ThreadLocal總結
??對于多線程資源共享的問題,同步機制采用了“以時間換空間”的方式:訪問串行化、對象共享化;而ThreadLocal
采用了“以空間換時間”的方式:訪問并行化,對象獨享化。前者僅提供一份變量,讓不同的線程排隊訪問;而后者為每個線程都提供了一份變量,因此可以同時訪問而互不影響。
Spring的事務管理
??Spring為事務管理提供了一致的編程模板,在高層次建立了統一的事務抽象。也就是說,不管是選擇Spring JDBC、Hibernate、JPA還是選擇Mybatis,Spring都可以讓用戶用統一的編程模型進行事務管理。
??像Spring DAO為不同的持久化實現提供了模板類一樣,Spring事務管理繼承了這一風格,也提供了事務模板了TransactionTemplate
,通過它并配合使用事務回調TransactionCallback
指定具體的持久化操作,就可以通過編程方式實現事務管理,而無需關注資源獲取、復用、釋放、事務同步和異常處理等操作。
??Spring事務管理的亮點在于聲明式事務管理。Spring允許通過聲明方式,在IOC配置中指定事務的邊界和事務屬性,Spring自動在指定的事務邊界上應用事務屬性。
事務管理的核心接口
??在Spring事務管理SPI(Service Provider Interface)的抽象層主要包括3個接口,分別是PlatformTransactionManager
、TransactionDefinition
和TransactionStatus
,它們位于org.springframework.transaction
包中,三者間的關系如下:
??
TransactionDefinition
用于描述事務的隔離級別、超時時間、是否為只讀事務和事務傳播規則等控制事務具體行為的事務屬性,這些事務實現可以通過XML配置或注解描述提供,也可以通過手工編程的方式設置。??
PlatformTransactionManager
根據TransactionDefinition
提供的事務屬性配置信息創建事務,并用TransactionStatus
描述這個激活事務的狀態。下面分別描述這些接口:
TransactionDefinition
??該接口定義了Spring兼容的事務屬性,這些屬性對事務管理控制的若干方面進行配置。
- 事務隔離:當前事務和其他事務的隔離程度。
- 事務傳播:通常在一個事務中執行的所有代碼都會允許在同一事務上下文中。
- 事務超時:事務在超時前能允許多久,超過時間后,事務被回滾。
-
只讀狀態:只讀事務不修改任何數據,資源事務管理者可以針對可讀事務應用一些優化措施,提高運行性能。
??Spirng允許通過XML或注解元數據的方式為一個有事務要求的服務類方法配置事務屬性,這些信息作為Spring事務管理框架的“輸入”,Spring將自動按事務屬性信息的指示,為目標方法提供相應的事務支持。
TransactionStatus
??該接口代表一個事務的具體運行狀態。事務管理者可以通過該接口獲取事務運行期的狀態信息,也可以通過該接口間接地回滾事務,它相比于拋出異常時回滾事務的方式更具可控性。
PlatformTransactionManager
??通過JDBC的事務管理知識可以知道,事務只能被提交或回滾(或回滾到某個保存點后提交),而該接口很好地描述了事務管理這個概念,解釋見代碼注釋:
public interface PlatformTransactionManager {
//該方法根據事務定義信息從事務環境中返回一個已存在的事務,或者創建一個新的事務,并用TransacitonStatus描述這個事務的狀態
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
//根據事務的狀態提交事務。如果事務已經被標識為rollback-only,則該方法將執行一個回滾事務的操作
void commit(TransactionStatus var1) throws TransactionException;
//將事務回滾。當commit()方法拋出異常時,該方法會被隱式調用
void rollback(TransactionStatus var1) throws TransactionException;
}
事務管理器實現類
??Spring將事務管理委托給底層具體的持久化實現框架來完成。因此為不同的框架提供了PlatformTransactionManager
接口的實現類,如下圖
??這些事務管理器都是對特定事務實現框架的代理,這樣就可以通過Spring所提交的高級抽象對不同種類的事務實現使用相同的方式進行管理,而不同關心具體的實現。
??要實現事務管理,首先要在Spring中配置好相應的事務管理器,為事務管理器指定數據資源及一些其他事務管理控制屬性,下面列出常見框架的配置。
1.Spring JDBC 和Mybatis框架配置
??如果使用Spring JDBC或Mybatis,由于它們都基于數據源的Connection訪問數據庫,所以可以使用DataSourceTransactionManager
,只要在Spring中進行如下配置即可:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.加載指定文件以配置數據庫相關參數屬性:${xxx}-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--2.定義數據源-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClass}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
/>
<!--3.基于相應數據源的事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
</beans>
2.Hibernate框架配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.加載指定文件以配置數據庫相關參數屬性:${xxx}-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--2.定義數據源,配置連接池屬性和c3p0私有屬性-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
p:driverClass="${jdbc.driverClass}"
p:jdbcUrl="${jdbc.url}"
p:user="${jdbc.username}"
p:password="${jdbc.password}"
p:maxPoolSize="30"
p:minPoolSize="10"
p:autoCommitOnClose="false"
p:checkoutTimeout="10000"
p:acquireRetryAttempts="2"
/>
<!--3.配置SqlSessionFactoryBean對象:注入數據源,配置mybatis全局文件,掃描entity包,使用別名,掃描sql配置文件-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="classpath:mybatis-config.xml"
p:typeAliasesPackage="cn.wk.entity"
p:mapperLocations="classpath:mapper/*.xml"
/>
<!--4.配置掃描Dao接口包,動態實現Dao接口,注入到Spring容器中-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"
p:sqlSessionFactoryBeanName="sqlSessionFactory"
p:basePackage="cn.wk.dao"
/>
</beans>
事務同步管理器(暫時了解)
??Spring將JDBC的Connection、Hibernate的Session等訪問數據庫的連接或會話統稱為資源,這些資源在同一時刻是不能多線程共享的。為了讓DAO、Service類可以做到singleton,Spring的事務同步管理器類org.springframework.transaction.support.TransactionSynchronizationManager
使用ThreadLocal
為不同事務線程提供了獨立的資源副本,同時維護事務配置的屬性和運行狀態信息。
??Spring框架為不同的持久化技術提供了一套從TransactionSynchronizationManager
獲取對應線程綁定資源的工具類,如下圖:
??這些工具類都提供了靜態的方法,通過這些方法可以獲取和當前線程綁定的資源。
事務傳播行為
??當我們調用一個基于Spring 的Service接口方法(如UserService
的adduser()
方法)時,它將運行于Spring管理的事務環境中,Service接口方法可能會在內部調用其他的Service接口方法以共同完成一個完整的業務操作,因此就會產生服務接口方法嵌套調用的情況,Spring通過事務傳播行為控制當前的事務如何傳播到被簽到調用的目標服務接口方法中。
??Spring在TransactionDefinition
接口中規定了7中類型的事務傳播行為,它們規定了事務方法和事務方法發生嵌套調用時事務如何進行傳播,如下圖:
Spring事務管理的兩種方式
- 編程式事務管理:通過編寫代碼實現的事務管理,包括定義事務的開始、正常執行后的事務提交和異常時的事務回滾。
- 聲明式事務管理:通過AOP技術實現的事務管理,主要思想是將事務作為一個“切面”代碼單獨編寫,然后通過AOP技術將事務管理的“切面”植入到業務目標類中。
??聲明式事務管理的最大優點在于開發者無需通過編程的方式來管理事務,只需在配置文件中進行相關的事務規則聲明,就可以將事務應用到業務邏輯中。這使得開發人員可以更加專注于核心業務邏輯代碼的編寫,在一定程度上減少了工作量,提高了開發效率,所以在實際開發中,通常都推薦使用聲明式事務管理。
編程式的事務管理(略)
聲明式事務管理
??大多數Spring用戶選擇聲明式事務管理的功能,這種方式對代碼的侵入性最小,可以讓事務管理代碼完全從業務代碼中移除,非常復合非侵入式輕量級容器的理念。
??Spring的聲明式事務管理是通過SpringAOP實現的,通過事務的聲明式信息,Spring負責將事務管理增強邏輯東塔織入業務方法的相應連接點中。這些邏輯包括獲取線程綁定資源、開始事務、提交/回滾事務、進行異常轉換和處理等工作。
??在Spring早期版本中,用戶必須通過TransactionProxyFactoryBean
代理類對需要事務管理的業務類進行代理,以便實施事務功能的增強。現在我們可以通過aop/tx
命名空間聲明事務,因此代理類實施聲明式事務的方法基本不再使用。當然,了解TransactionProxyFactoryBean
有助于我們更直觀地理解Spring實施聲明式事務的內部工作原理,通過一個例子來了解:
1.創建一個業務Bean
@Service
@Transactional
public class BbtForum{
public ForumDao forumDao;
public TopicDao topicDao;
public PostDao postDao;
public void addTopic(Top topic){
topicDao.addTopic(topic);
postDao.addPost(topic.getPost());
}
public forum getForun(int forumId){
return forumDao.getForum(forumId);
}
public void updateForum(Forum forum){
forumDao.updateForum(forum);
}
public int getForumNum(){
return forumDao.getForumNum();
}
}
??該類有4個方法,我們希望addTopic
和updateForum()
方法擁有寫事務的能力,而其他兩個方法只需要有讀事務的能力就可以了。
2.使用TransactionProxyFactoryBean配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.加載指定文件以配置數據庫相關參數屬性:${xxx}-->
<context:property-placeholder location="classpath:db.properties"/>
<!--2.定義數據源-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClass}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
/>
<!--3.基于相應數據源的事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
<!--需要實施事務增強的業務Bean-->
<bean id="bbtForumTarget" class="cn.wk.chapter11.service.BbtForum"
p:forumDao-ref="forumDao"
p:topicDao-ref="topicDao"
p:postDao-ref="postDao"
/>
<!--使用事務代理工廠為目標業務Bean提供事務增強-->
<bean id="bbtForum" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
p:transactionManager-ref="transactionManager"
p:target-ref="bbtForumTarget">
<property name="transactionAttributes">
<props>
<!--只讀事務-->
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<!--可寫事務-->
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
??按照約定的習慣,需要事務增強的業務類一般將id取名為xxTarget,這可以在字面上表示該Bean是要被代理的目標Bean。
??通過TransactionProxyFactoryBean
對業務類進行代理,織入事務增強功能。首先,需要為該代理類指定事務管理器,這些事務管理器實現了PlatformTransactionManager
接口;其次,通過target屬性指定需要代理的目標Bean;最后,為業務Bean的不同方法配置事務屬性。Spring允許通過鍵值配置業務方法的事務屬性信息,鍵可以使用通配符,如get*
代表目標類中所有以get為前綴的方法,它匹配BbtForum
的getForum(int forumId)
和getForumNum()
方法;而key="*"
代表匹配BbtForum
接口的所有方法。
??<prop>
內的值為事務屬性信息,配置格式如下:
基于XML配置的聲明式事務
??使用TransactionProxyFactoryBean
代理工廠類為業務類添加事務支持缺點是配置過于繁瑣,所以Spring后來在配置中添加了一個tx命名空間,在配置文件中以明確結構化的方式定義事務屬性,大大提高了配置事務屬性的便利性,<tx:advice>
標簽用法格式如下:
??我們用tx和aop命名空間對前面基于FactoryBean的事務配置方式進行替換,代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-context.xsd">
<!--1.加載指定文件以配置數據庫相關參數屬性:${xxx}-->
<context:property-placeholder location="classpath:db.properties"/>
<!--2.定義數據源-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClass}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
/>
<!--3.基于相應數據源的事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<!--4.使用強大的切點表達式語言輕松定義目標方法-->
<aop:config>
<!--通過aop定義事務增強切面-->
<aop:pointcut id="serviceMethod" expression="execution(* cn.wk.chapter11.service.*Forum.*(..))"/>
<!--引用事務增強-->
<aop:advisor advice-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
<!--5.事務增強-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--屬性定義-->
<tx:attributes>
<tx:method name="get" read-only="false"/>
<tx:method name="add*" rollback-for="Exception"/>
<tx:method name="update"/>
</tx:attributes>
</tx:advice>
</beans>
??在這一過程中我們看到了3種角色:通過aop/tx定義的聲明式事務配置信息、業務Bean、Spring容器。Spring容器自動將第一者應用于第二者,從容器中返回的業務Bean已經是被織入事務增強的代理Bean,即第一者和第二者在配置時不直接發生關系。
??而在使用TransactionProxyFactoryBean
進行事務配置時,它需要直接通過target屬性引用目標業務Bean,結果造成目標業務Bean往往需要使用target進行命名,以避免和最終代理Bean名沖突。使用aop/tx方式后,業務Bean的名稱不需要做任何“配合性”的調整,aop直接通過切點表達式語言就可以對業務Bean進行定位。從這個意義聲來說,aop/tx
的配置方式對業務Bean是“無侵入”的,而代理類的配置顯然是“侵入式”的。
??在aop的命名空間中,通過切點表達式語言,將cn.wk.chapter11.service包下所有以Forum為后綴的類納入了需要進行事務增強的范圍,并配合<tx:advice>
和<aop:advisor>
完成了事務切面的定義。<aop:advisor>
引用的txAdvice增強是在tx命名空間上定義的。首先,事務增強一定需要一個事務管理器的支持,<tx;advice>
通過transaction
屬性引用了定義好的事務管理器。曾經摻雜在一起,以逗號分割字符串定義的事務屬性,現在變成了一個清晰的XML片段,十分簡潔。<tx:method>
元素用于的屬性如下:
??如果需要為不同的業務類Bean應用不同的事務管理風格,則可以在
<aop;config>
中定義另外多套事務切面,具體的配置方法在現有基礎上演繹即可。
基于注解的聲明式事務(常用)
??除了基于XML的事務配置,Spring還提供了基于注解的事務配置,即通過@Transactional
對需要事務增強的Bean接口、實現類或方法進行標注;在容器中配置基于注解的事務增強驅動,即可啟用基于注解的聲明式事務。現在項目中基本都采用這種配置。下面為配置步驟:
1.在需要事務管理的業務Bean前加上一個@Transactional
注解:
@Service
@Transactional
public class BbtForum{
.......
}
2.在配置文件加入標簽
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-context.xsd">
<!--1.掃描service包注冊以注解方式聲明的Bean-->
<context:component-scan base-package="cn.wk.crm.service"/>
<!--2.配置事務管理器,注入數據庫連接池-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"
/>
<!--3.注解驅動會對標注@Transactional的Bean進行加工處理,以織入事務管理切面-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
注:
數據源一般在dao
層的xml文件配置,此處直接引用了,詳見Spring第三篇文章。
??在默認情況下,<tx:annotation-driven>
會默認使用名為transactionManager
的事務管理器。所以,如果用戶的事務管理器id為transactionManager
,則可以進一步簡化配置為:
<tx:annotation-driven/>
@Transactional注解屬性
??和XML配置方式一樣,該注解也擁有一組普適性很強的默認事務屬性,往往可以直接使用這些默認屬性,具體如下:
-
事務傳播行為:
PROPAGATION_REQUIRED
-
事務隔離級別:
ISOLATION_DEFAULT
- 讀寫事務屬性:讀/寫事務。
- 超時時間:依賴于底層的事務系統的默認值。
- 回滾設置:任何運行期異常引發回滾,任何檢查型異常不會引發回滾。
??這些默認設置在大多數情況下都適用,當然,Spring也運行通過手工設定屬性值覆蓋默認值。
@Transactional注解的兩個位置
??@Transactional注解可以在類上使用,表示事務的設置對整個類上的方法都起作用,也可以在方法處使用,表示事務的設置只對該方法有效,如果即在類上加上該注解,又在類中方法上加上該注解,則方法上的注解會覆蓋類上的注解。
參考資料
《精通Spring 4.x 企業應用開發》