交易支援
習慣使用關聯式資料庫的程式設計師,在接觸 LDAP 世界時,經常對於沒有交易的概念感到驚訝。協定中並未指定交易,也沒有 LDAP 伺服器支援交易。Spring LDAP 認識到這可能是一個主要問題,因此提供了在 LDAP 資源上進行用戶端補償交易的支援。
LDAP 交易支援由 ContextSourceTransactionManager
提供,這是一個 PlatformTransactionManager
實作,用於管理 LDAP 操作的 Spring 交易支援。與其協作者一起,它會追蹤在交易中執行的 LDAP 操作,記錄每個操作之前的狀態,並採取步驟在交易需要回滾時還原初始狀態。
除了實際的交易管理之外,Spring LDAP 交易支援還確保在同一個交易中始終使用相同的 DirContext
實例。也就是說,DirContext
實際上在交易完成之前不會關閉,從而實現更有效率的資源使用。
雖然 Spring LDAP 用於提供交易支援的方法在許多情況下都已足夠,但它絕不是傳統意義上的「真實」交易。伺服器完全不知道交易的存在,因此(例如)如果連線中斷,則無法回滾交易。雖然應該仔細考慮這一點,但也應該注意到,替代方案是在沒有任何交易支援的情況下運作。Spring LDAP 的交易支援幾乎已達到最佳狀態。 |
用戶端交易支援除了原始操作所需的工作之外,還增加了一些額外負荷。雖然在大多數情況下,這種額外負荷不應成為問題,但如果您的應用程式在同一個交易中沒有執行多個 LDAP 操作(例如,modifyAttributes 後面接著 rebind ),或者如果不需要與 JDBC 資料來源進行交易同步(請參閱 JDBC 交易整合),那麼使用 LDAP 交易支援幾乎沒有任何好處。 |
組態
如果您習慣於組態 Spring 交易,那麼組態 Spring LDAP 交易應該看起來非常熟悉。您可以使用 @Transactional
註解您的交易類別,建立 TransactionManager
實例,並在您的 Bean 組態中包含 <tx:annotation-driven>
元素。以下範例示範了如何執行此操作
<ldap:context-source
url="ldap://localhost:389"
base="dc=example,dc=com"
username="cn=Manager"
password="secret" />
<ldap:ldap-template id="ldapTemplate" />
<ldap:transaction-manager>
<!--
Note this default configuration will not work for more complex scenarios;
see below for more information on RenamingStrategies.
-->
<ldap:default-renaming-strategy />
</ldap:transaction-manager>
<!--
The MyDataAccessObject class is annotated with @Transactional.
-->
<bean id="myDataAccessObject" class="com.example.MyRepository">
<property name="ldapTemplate" ref="ldapTemplate" />
</bean>
<tx:annotation-driven />
...
雖然此設定適用於大多數簡單的使用案例,但某些更複雜的場景需要額外的組態。具體來說,如果您需要在交易中建立或刪除子樹,則需要使用替代的 TempEntryRenamingStrategy ,如 重新命名策略 中所述。 |
在實際情況中,您可能會在服務物件層級而不是儲存庫層級應用交易。前面的範例示範了一般概念。
JDBC 交易整合
對 LDAP 進行操作時,常見的使用案例是某些資料儲存在 LDAP 樹狀結構中,而其他資料則儲存在關聯式資料庫中。在這種情況下,交易支援變得更加重要,因為不同資源的更新應該同步。
雖然不支援實際的 XA 交易,但透過將 data-source-ref
屬性提供給 <ldap:transaction-manager>
元素,可以提供概念上將 JDBC 和 LDAP 存取包裝在同一個交易中的支援。這會建立一個 ContextSourceAndDataSourceTransactionManager
,然後虛擬地將這兩個交易作為一個交易進行管理。當執行提交時,操作的 LDAP 部分始終首先執行,以便在 LDAP 提交失敗時可以回滾這兩個交易。交易的 JDBC 部分的管理方式與 DataSourceTransactionManager
完全相同,只是不支援巢狀交易。以下範例顯示了一個帶有 data-source-ref
屬性的 ldap:transaction-manager
元素
<ldap:transaction-manager data-source-ref="dataSource" >
<ldap:default-renaming-strategy />
<ldap:transaction-manager />
提供的支援都是用戶端。包裝的交易不是 XA 交易。不執行兩階段提交,因為 LDAP 伺服器無法對其結果進行投票。 |
您可以透過將 session-factory-ref
屬性提供給 <ldap:transaction-manager>
元素,為 Hibernate 整合完成相同的操作,如下所示
<ldap:transaction-manager session-factory-ref="dataSource" >
<ldap:default-renaming-strategy />
<ldap:transaction-manager />
LDAP 補償交易說明
Spring LDAP 透過記錄每次修改操作(bind
、unbind
、rebind
、modifyAttributes
和 rename
)之前 LDAP 樹狀結構中的狀態來管理補償交易。這讓系統可以在交易需要回滾時執行補償操作。
在許多情況下,補償操作非常簡單明瞭。例如,bind
操作的補償回滾操作是解除綁定條目。然而,由於 LDAP 資料庫的一些特殊特性,其他操作需要不同且更複雜的方法。具體來說,並非始終可以取得條目的所有 Attributes
的值,這使得上述策略不足以應對(例如)unbind
操作。
這就是為什麼在 Spring LDAP 管理的交易中執行的每個修改操作在內部都會拆分為四個不同的操作:記錄操作、準備操作、提交操作和回滾操作。下表描述了每個 LDAP 操作
LDAP 操作 | 記錄 | 準備 | 提交 | 回滾 |
---|---|---|---|---|
|
記錄要綁定條目的 DN。 |
綁定條目。 |
無操作。 |
使用記錄的 DN 解除綁定條目。 |
|
記錄原始 DN 和目標 DN。 |
重新命名條目。 |
無操作。 |
將條目重新命名回其原始 DN。 |
|
記錄原始 DN 並計算臨時 DN。 |
將條目重新命名到臨時位置。 |
解除綁定臨時條目。 |
將條目從臨時位置重新命名回其原始 DN。 |
|
記錄原始 DN 和新的 |
將條目重新命名到臨時位置。 |
在原始 DN 綁定新的 |
將條目從臨時位置重新命名回其原始 DN。 |
|
記錄要修改條目的 DN,並為要完成的修改計算補償 |
執行 |
無操作。 |
透過使用計算出的補償 |
Spring LDAP 交易支援的內部運作的更詳細描述可在 Javadoc 中找到。
重新命名策略
如前一節表格中所述,某些操作的交易管理需要先暫時重新命名受操作影響的原始條目,然後才能在提交中進行實際修改。條目的臨時 DN 的計算方式由 TempEntryRenamingStrategy
管理,該策略在組態中 <ldap:transaction-manager >
宣告的子元素中指定。Spring LDAP 包含兩個實作
-
DefaultTempEntryRenamingStrategy
(預設):透過使用<ldap:default-renaming-strategy />
元素指定。將尾碼新增至條目 DN 的最低有效部分。例如,對於cn=john doe, ou=users
的 DN,此策略傳回cn=john doe_temp, ou=users
的臨時 DN。您可以透過設定temp-suffix
屬性來組態尾碼。 -
DifferentSubtreeTempEntryRenamingStrategy
:透過使用<ldap:different-subtree-renaming-strategy />
元素指定。它將子樹 DN 附加到 DN 的最低有效部分。這樣做會使所有臨時條目都放置在 LDAP 樹狀結構中的特定位置。臨時子樹 DN 透過設定subtree-node
屬性來組態。例如,如果subtree-node
是ou=tempEntries
,並且條目的原始 DN 是cn=john doe, ou=users
,則臨時 DN 是cn=john doe, ou=tempEntries
。請注意,組態的子樹節點需要存在於 LDAP 樹狀結構中。
DefaultTempEntryRenamingStrategy 在某些情況下不起作用。例如,如果您計劃執行遞迴刪除,則需要使用 DifferentSubtreeTempEntryRenamingStrategy 。這是因為遞迴刪除操作實際上包括深度優先地個別刪除子樹中的每個節點。由於您無法重新命名具有任何子節點的條目,並且 DefaultTempEntryRenamingStrategy 會將每個節點保留在同一個子樹中(名稱不同),而不是實際刪除它,因此此操作將會失敗。如有疑問,請使用 DifferentSubtreeTempEntryRenamingStrategy 。 |