javascript
Spring @Transactional实际如何工作?
在本文中,我們將深入探討Spring事務(wù)管理。 我們將討論@Transactional在@Transactional如何真正工作。 其他即將發(fā)布的帖子將包括:
- 如何使用傳播和隔離等功能
- 主要陷阱是什么以及如何避免它們
JPA和事務(wù)管理
重要的是要注意,JPA本身不提供任何類型的聲明式事務(wù)管理。 在依賴項(xiàng)注入容器之外使用JPA時(shí),開發(fā)人員需要以編程方式處理事務(wù):
UserTransaction utx = entityManager.getTransaction(); try { utx.begin(); businessLogic();utx.commit(); } catch(Exception ex) { utx.rollback(); throw ex; }這種管理事務(wù)的方式使代碼中的事務(wù)范圍非常清楚,但是有一些缺點(diǎn):
- 它是重復(fù)性的并且容易出錯(cuò)
- 任何錯(cuò)誤都會(huì)產(chǎn)生很大的影響
- 錯(cuò)誤很難調(diào)試和重現(xiàn)
- 這降低了代碼庫的可讀性
- 如果此方法調(diào)用另一個(gè)事務(wù)方法怎么辦?
使用Spring @Transactional
使用Spring @Transactional ,上述代碼簡化為:
@Transactionalpublic void businessLogic() {... use entity manager inside a transaction ...}這更加方便和易讀,是當(dāng)前在Spring中推薦的處理事務(wù)的推薦方法。
通過使用@Transactional ,可以自動(dòng)處理許多重要方面,例如事務(wù)傳播。 在這種情況下,如果businessLogic()調(diào)用了另一個(gè)事務(wù)方法,則該方法可以選擇加入正在進(jìn)行的事務(wù)。
潛在的不利之處在于,這種強(qiáng)大的機(jī)制隱藏了幕后發(fā)生的事情,從而在無法正常工作時(shí)很難進(jìn)行調(diào)試。
@Transactional的關(guān)鍵點(diǎn)之一是要考慮兩個(gè)單獨(dú)的概念,每個(gè)概念都有自己的范圍和生命周期:
- 持久性環(huán)境
- 數(shù)據(jù)庫事務(wù)
事務(wù)注釋本身定義了單個(gè)數(shù)據(jù)庫事務(wù)的范圍。 數(shù)據(jù)庫事務(wù)在持久性上下文的范圍內(nèi)發(fā)生。
持久性上下文在JPA中是EntityManager ,使用Hibernate Session在內(nèi)部實(shí)現(xiàn)(當(dāng)使用Hibernate作為持久性提供程序時(shí))。
持久性上下文只是一個(gè)同步器對象,該對象跟蹤一組有限的Java對象的狀態(tài),并確保最終將這些對象上的更改持久化回到數(shù)據(jù)庫中。
這與數(shù)據(jù)庫事務(wù)的概念非常不同。 一個(gè)實(shí)體管理器可用于多個(gè)數(shù)據(jù)庫事務(wù) ,而實(shí)際上經(jīng)常是這樣。
EntityManager何時(shí)跨多個(gè)數(shù)據(jù)庫事務(wù)?
最常見的情況是,當(dāng)應(yīng)用程序使用“打開視圖中的會(huì)話”模式來處理延遲的初始化異常時(shí),請參見上一篇博客文章,以了解其優(yōu)缺點(diǎn) 。
在這種情況下,在視圖層中運(yùn)行的查詢與用于業(yè)務(wù)邏輯的查詢不在單獨(dú)的數(shù)據(jù)庫事務(wù)中,但是它們是通過同一實(shí)體管理器進(jìn)行的。
另一種情況是,開發(fā)人員將持久性上下文標(biāo)記為PersistenceContextType.EXTENDED ,這意味著它可以承受多個(gè)請求。
什么定義了EntityManager vs Transaction關(guān)系?
這實(shí)際上是應(yīng)用程序開發(fā)人員的選擇,但是使用JPA實(shí)體管理器的最常見方法是使用“每個(gè)應(yīng)用程序事務(wù)的實(shí)體管理器”模式。 這是注入實(shí)體管理器的最常見方法:
@PersistenceContextprivate EntityManager em;在這里,默認(rèn)情況下,我們處于“每個(gè)事務(wù)實(shí)體管理器”模式。 在這種模式下,如果我們在@Transactional方法內(nèi)使用此實(shí)體管理器,則該方法將在單個(gè)數(shù)據(jù)庫事務(wù)中運(yùn)行。
@PersistenceContext如何工作?
想到的一個(gè)問題是,鑒于實(shí)體管理器的生命周期如此短,并且每個(gè)請求通常有多個(gè)實(shí)體,因此@PersistenceContext如何在容器啟動(dòng)時(shí)僅注入一次實(shí)體管理器。
答案是:它不能: EntityManager是一個(gè)接口,注入到bean中的不是實(shí)體管理器本身,而是上下文感知的代理 ,它將在運(yùn)行時(shí)委派給具體的實(shí)體管理器。
通常,用于代理的具體類是SharedEntityManagerInvocationHandler ,可以在調(diào)試器的幫助下進(jìn)行確認(rèn)。
@Transactional如何工作?
實(shí)現(xiàn)EntityManager的持久性上下文代理不是使聲明式事務(wù)管理工作所需的唯一組件。 實(shí)際上需要三個(gè)獨(dú)立的組件:
- EntityManager代理本身
- 交易方面
- 交易經(jīng)理
讓我們逐一檢查一下,看看它們?nèi)绾蜗嗷プ饔谩?
交易方面
事務(wù)方面是在注釋的業(yè)務(wù)方法之前和之后都被調(diào)用的“周圍”方面。 實(shí)現(xiàn)方面的具體類是TransactionInterceptor 。
事務(wù)方面有兩個(gè)主要職責(zé):
- 在“之前”時(shí)刻,該方面提供了一個(gè)掛鉤點(diǎn),用于確定要調(diào)用的業(yè)務(wù)方法是否應(yīng)在正在進(jìn)行的數(shù)據(jù)庫事務(wù)范圍內(nèi)運(yùn)行,或者是否應(yīng)該啟動(dòng)新的單獨(dú)事務(wù)。
- 在“之后”時(shí)刻,方面需要確定是應(yīng)該提交事務(wù),回滾事務(wù)還是保持運(yùn)行。
在“之前”時(shí)刻,事務(wù)方面本身不包含任何決策邏輯,如果需要,則啟動(dòng)新事務(wù)的決策將委托給事務(wù)管理器。
交易經(jīng)理
交易經(jīng)理需要提供以下兩個(gè)問題的答案:
- 是否應(yīng)該創(chuàng)建一個(gè)新的實(shí)體管理器?
- 是否應(yīng)該啟動(dòng)新的數(shù)據(jù)庫事務(wù)?
這需要在調(diào)用事務(wù)方面“之前”邏輯時(shí)確定。 交易經(jīng)理將根據(jù)以下內(nèi)容做出決定:
- 一項(xiàng)交易是否已經(jīng)進(jìn)行的事實(shí)
- 事務(wù)方法的傳播屬性(例如, REQUIRES_NEW始終啟動(dòng)新事務(wù))
如果交易經(jīng)理決定創(chuàng)建新交易,則它將:
- 創(chuàng)建一個(gè)新的實(shí)體經(jīng)理
- 將實(shí)體管理器綁定到當(dāng)前線程
- 從數(shù)據(jù)庫連接池中獲取連接
- 將連接綁定到當(dāng)前線程
實(shí)體管理器和連接都使用ThreadLocal變量綁定到當(dāng)前線程。
它們在事務(wù)運(yùn)行時(shí)存儲(chǔ)在線程中,并且當(dāng)不再需要它們時(shí),由事務(wù)管理器來清理它們。
程序中需要當(dāng)前實(shí)體管理器或連接的任何部分都可以從線程中檢索它們。 正是這樣做的一個(gè)程序組件是EntityManager代理。
EntityManager代理
最后一步是EntityManager代理(我們之前已經(jīng)介紹過)。 以業(yè)務(wù)方法為例
entityManager.persist() ,此調(diào)用未直接調(diào)用實(shí)體管理器。
相反,業(yè)務(wù)方法調(diào)用代理,該代理從線程(事務(wù)管理器放置在該線程中)中檢索當(dāng)前實(shí)體管理器。
現(xiàn)在知道了@Transactional機(jī)制的哪些移動(dòng)部分,讓我們@Transactional這項(xiàng)工作所需的常規(guī)Spring配置。
全部放在一起
讓我們研究一下如何設(shè)置使事務(wù)注釋正確工作所需的三個(gè)組件。 我們首先定義實(shí)體管理器工廠。
這將允許通過持久性上下文注釋注入Entity Manager代理:
@Configurationpublic class EntityManagerFactoriesConfiguration {@Autowiredprivate DataSource dataSource;@Bean(name = "entityManagerFactory")public LocalContainerEntityManagerFactoryBean emf() {LocalContainerEntityManagerFactoryBean emf = ...emf.setDataSource(dataSource);emf.setPackagesToScan(new String[] {"your.package"});emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());return emf;}}下一步是配置事務(wù)管理器,并將事務(wù)方面應(yīng)用于@Transactional注釋的類:
@Configuration@EnableTransactionManagementpublic class TransactionManagersConfig {@AutowiredEntityManagerFactory emf;@Autowiredprivate DataSource dataSource;@Bean(name = "transactionManager")public PlatformTransactionManager transactionManager() {JpaTransactionManager tm = new JpaTransactionManager();tm.setEntityManagerFactory(emf);tm.setDataSource(dataSource);return tm;}}注釋@EnableTransactionManagement告訴Spring帶有@Transactional注釋的類應(yīng)使用Transactional Aspect包裝。 這樣,@ @Transactional現(xiàn)在可以使用了。
結(jié)論
Spring聲明式事務(wù)管理機(jī)制非常強(qiáng)大,但是很容易被濫用或錯(cuò)誤配置。
在機(jī)制無法正常工作或無法正常工作的情況下進(jìn)行故障排除時(shí),了解其內(nèi)部工作方式將很有幫助。
要牢記的最重要的一點(diǎn)是,實(shí)際上有兩個(gè)概念需要考慮:數(shù)據(jù)庫事務(wù)和持久性上下文,每個(gè)都有其自身不容易顯現(xiàn)的生命周期。
將來的帖子將討論事務(wù)注釋的最常見陷阱以及如何避免它們。
翻譯自: https://www.javacodegeeks.com/2014/06/how-does-spring-transactional-really-work.html
總結(jié)
以上是生活随笔為你收集整理的Spring @Transactional实际如何工作?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑k歌麦克风推荐(推荐一款k歌麦克风)
- 下一篇: 使用Spring MVC时的常见错误