對於第二個問題,涉及到事務的傳播級別,定義如下:
PROPAGATION_REQUIRED-- 如果當前沒有事務,就新建一個事務。這是最常見的選擇。
PROPAGATION_SUPPORTS-- 如果當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY-- 如果當前沒有事務,就丟擲異常。
PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。
PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則丟擲異常。
在開啟事務之前,正常情況下需要做兩個事情
一:獲取當前事務上下文資訊
二:獲取將要開啟事務的傳播屬性
根據以上兩個資訊,來判斷程式的處理方式,具體方式如下:
而處理流程則是如下:
其中上圖示中英文簡稱對應的事務傳播屬性如下:
RE: PROPAGATION_REQUIRED-- 如果當前沒有事務,就新建一個事務。這是最常見的選擇。
SPT PROPAGATION_SUPPORTS-- 如果當前沒有事務,就以非事務方式執行。
MA: PROPAGATION_MANDATORY-- 如果當前沒有事務,就丟擲異常。
RE_NEW: PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。
NOT_SPT: PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
NEVER: PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則丟擲異常。
通過上面發現,只有新建立資源的時候,才會開啟事務,在其他的情況下,只需要返回事務狀態資訊就可以了。其實這個狀態資訊,就是事務的上下文資訊。
事務上下文
通過上面的分析,每次啟動事務的時候,都會判斷當前是否存在事務,要麼丟擲異常,否則都會創新事務上下文,但是對於資料來源的處理方式則是不一樣的,這個要根據當前事務傳播屬性和新的事務傳播屬性共同決定。
事務上下文資訊到底是什麼,這個完全是可以自定義的,在spring中,主要是表現為TransactionStatus,也就是事務狀態資訊。裡面儲存了事務相關的資訊,
//事務物件資訊,使用普通資料來源的話,是DataSourceTransactionObject物件,儲存//了事務對應的連線資訊 private final Object transaction; //是否是新開啟的事務資訊,只有呼叫了開啟事務的方法,這個才為true private final boolean newTransaction; //這個是事務同步器,不是事務要關心的,spring在事務提交之前或者之後座的hook private final boolean newSynchronization; //是否是隻讀事務 private final boolean readOnly; //日誌debug資訊,完全沒有放在這裡 private final boolean debug; //掛起的事務資訊,如果沒有,則為空 private final Object suspendedResources;
通過事務狀態資訊,就可以完全知道當前事務的所有資訊,包括事務的對應的資料來源連線資訊,是否是新建立的事務,是否是隻讀事務,以及之前掛起的事務資訊。這些對事務管理器起來說,都是必須的資訊。但是個人覺得也存在一些問題,首先spring這個事務狀態資訊有兩個使用者,一個是spring本身事務管理器使用,另外一個是應用程式介面。對應用程式介面暴露出來的狀態資訊以及內部使用的事務上下文資訊應該隔離出來,避免應用程式人為的修改了事務上下文的屬性資訊。當然,可以用過介面的方式進行避免,但是如果知道實現原理的話,完全可以通過強制轉化為實現物件,從而破壞事務其他的資訊導致程式異常。當然,正常情況下不太可能有人會如此無聊。
從上面的分析來看,spring的事務管理器(這裡都特指DataSourceTransactionManager)主要的工作流程就是建立事務資訊,繫結資料來源,獲取資料庫連線,提交活回滾事務,釋放資料庫連線,解綁資料來源。其實整個事務管理器做的事情無非就是這些。讓應用者更關注業務邏輯,而不是複雜的事務管理。
DataSourceTransactionManager事務管理器本身實現了ResouceManager的功能,就是返回對應的其註冊的datasrouce。這是一種一對一的對映關係,也就是說一個事務管理器只能註冊一個資料來源,不支援多資料來源的管理。一旦事務管理器開啟事務,就和具體的資料來源繫結了,你只能通過其對應的資料來源獲取資料庫連線。所以在事務上下文裡面操作多個資料庫,是不可能的。同時也只支援單一物理資料來源,也就是說一個資料來源只能返回同一個資料庫連線,不支援在同一個事務裡面通過同一個邏輯資料來源跨越多個物理庫操作。下面的操作想通過ProxyDataSource切換實際的資料來源的方式無法實現的。
for(String dbName : dbNames){
DataSourceContextHolder.set(“dbName”);
doSomeThing();
DataSourceContextHolder.clear();
}
想要支援跨庫的事務操作,可以通過以下幾種方式操作:
1 使用JtaTransactionManager,通過jta服務提供商來實現跨庫事務
2 改寫ProxyDataSource,通過返回其自己實現的Connection來實現跨庫的事務。簡單的說,返回一個邏輯的Connection,這個connection本身持有多個物理connection
3 自己實現TransactionManager,可以註冊多個資源管理器,自己對多個資料來源進行管理。
事務上下文的擴充套件
正常情通過況下,事務上下文資訊都是儲存在記憶體之中,相當於只能夠支援單個jvm。可以想象一下,假設事務管理器把事務上下文資訊持久化,並且通過遠端呼叫的方式,把事務上下文資訊傳遞給另外一個jvm,通過這樣的設計思想,可以支援跨jvm間的事務一致性,也就是我們所說的分散式系統的事務。當然,這只是一中簡單的想法,具體的實現會相當複雜,需要考慮點也有很多。