Transaction Support
习惯于使用关系数据库的程序员在进入 LDAP 领域时常常会对没有事务这一概念感到惊讶。协议中未指定该概念,没有 LDAP 服务器支持它。Spring LDAP 认识到这可能是一个主要问题,因此为 LDAP 资源中的客户端补偿事务提供了支持。
LDAP 事务支持由 ContextSourceTransactionManager
提供,它是一个 PlatformTransactionManager
实现,管理 LDAP 操作的 Spring 事务支持。它与其协作者一起跟踪事务中执行的 LDAP 操作,记录每个操作之前的状态,并采取措施在需要回滚事务时恢复初始状态。
除了实际的事务管理之外,Spring LDAP 事务支持还确保在整个事务期间使用相同的 DirContext
实例。也就是说,在事务完成之前实际上不会关闭 DirContext
,从而可以更有效地使用资源。
尽管 Spring LDAP 提供的事务支持适用于许多情况,但它绝不是传统意义上的 “real” 事务。服务器完全不感知事务,所以(例如)如果连接断开,就无法回滚该事务。虽然需要仔细考虑这一点,还应注意,替代方法是在没有任何事务支持的情况下进行操作。Spring LDAP 的事务支持几乎是尽善尽美了。
客户端事务支持除了原始操作所需的工作之外,还增加了额外的开销。虽然大多数情况下不用担心这种开销,如果您的应用程序不执行同一事务中的多个 LDAP 操作(例如,执行 |
Configuration
如果已经习惯了配置 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 />
...
虽然此设置适用于大多数简单用例,一些更复杂的方案需要额外的配置。具体来说,如果您需要在事务中创建或删除子树,则需要使用替代的 |
在实际情况中,您可能更愿意在服务对象级别而非存储库级别应用事务。之前的示例展示了这个基本思想。
JDBC Transaction Integration
使用 LDAP 工作时的常见用例是,部分数据存储在 LDAP 树中,但其他数据存储在关系数据库中。在这种情况下,因为不同资源更新应该同步,事务支持变得更加重要。
尽管不支持实际的 XA 事务,但可以提供支持,通过向 <ldap:transaction-manager>
元素提供 data-source-ref
属性来在同一个事务中概念性地包装 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 服务器无法对提交结果表决。 |
您可以通过下列方法,为 Hibernate 集成执行相同操作:向 <ldap:transaction-manager>
元素提供 session-factory-ref
属性:
<ldap:transaction-manager session-factory-ref="dataSource" >
<ldap:default-renaming-strategy />
<ldap:transaction-manager />
LDAP Compensating Transactions Explained
Spring LDAP 记录每个修改操作 (bind
、unbind
、rebind
、modifyAttributes
和 rename
) 之前的 LDAP 树状态,从而管理补偿性事务。如果需要回滚事务,这会让系统执行补偿性操作。
在很多情况下,补偿性操作非常简单。例如,bind
操作的补偿性回滚操作是取消绑定条目。但是,由于 LDAP 数据库的某些特定特征,其他操作需要采用不同的、更为复杂的方法。具体来说,并非总能获得条目的所有 Attributes
的值,这使得前述策略不够用(例如)unbind
操作。
这就是在 Spring LDAP 管理的事务中执行的每个修改操作在内部都被分成四个不同操作的原因:一个记录操作、一个准备操作、一个提交操作和一个回滚操作。下表描述了每个 LDAP 操作:
LDAP Operation | Recording | Preparation | Commit | Rollback |
---|---|---|---|---|
|
记录要绑定的条目 DN。 |
Bind the entry. |
No operation. |
使用记录的 DN 解绑条目。 |
|
记录源 DN 和目标 DN。 |
Rename the entry. |
No operation. |
将条目重命名回其原始 DN。 |
|
记录原始 DN 并计算临时 DN。 |
将条目重命名为临时位置。 |
Unbind the temporary entry. |
将条目从临时位置重命名回其原始 DN。 |
|
记录原始 DN 和新 |
将条目重命名为临时位置。 |
在原始 DN 上绑定新 |
将条目从临时位置重命名回其原始 DN。 |
|
记录要修改的条目的 DN,并为要进行的修改计算补偿 |
Perform the |
No operation. |
通过使用计算得出的补偿性操作来执行 |
Spring LDAP 事务支持的内部工作机制的更详细说明在 Javadoc 中提供。
Renaming Strategies
如前一节中的表格所述,有些操作的事务管理需要在提交中可以进行实际修改之前,临时重命名该操作影响的原始条目。计算条目的临时 DN 的方式由配置中 <ldap:transaction-manager >
声明的子元素中指定的 TempEntryRenamingStrategy
管理。Spring LDAP 包含两个实现:
-
DefaultTempEntryRenamingStrategy
(默认):使用<ldap:default-renaming-strategy />`元素指定。添加到条目 DN 最不重要的部分的后缀。例如,对于 DN `cn=john doe, ou=users
,此策略返回一个临时 DNcn=john doe_temp, ou=users
。可以通过设置 `temp-suffix`属性来配置后缀。 -
DifferentSubtreeTempEntryRenamingStrategy
:使用<ldap:different-subtree-renaming-strategy />`元素指定。将子树 DN 追加到 DN 最不重要的部分。这样做会使所有临时条目都放置在 LDAP 树中的特定位置。通过设置 `subtree-node`属性来配置临时子树 DN。例如,如果 `subtree-node`是 `ou=tempEntries
,并且条目的原始 DN 是cn=john doe, ou=users
,则临时 DN 为cn=john doe, ou=tempEntries
。请注意,配置的子树节点需要存在于 LDAP 树中。
|