事務支援
習慣於關係型資料庫的程式設計師來到 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://: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,如 重新命名策略 中所述。 |
在實際情況中,您可能會在服務物件級別而不是儲存庫級別應用事務。前面的示例演示了通用思想。
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 的計算方式由配置中 <ldap:transaction-manager > 宣告的子元素中指定的 TempEntryRenamingStrategy 管理。Spring LDAP 包含兩種實現:
-
DefaultTempEntryRenamingStrategy(預設):使用<ldap:default-renaming-strategy />元素指定。將字尾新增到條目 DN 的最低有效部分。例如,對於 DNcn=john doe, ou=users,此策略返回的臨時 DN 為cn=john doe_temp, ou=users。您可以透過設定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。 |