事务支持
习惯于使用关系数据库进入 LDAP 世界的程序员经常对没有事务概念的事实表示惊讶。 协议中未指定它,并且没有 LDAP 服务器支持它。 认识到这可能是一个主要问题, Spring LDAP 提供了对 Client 端的支持,补偿 LDAP 资源上的事务。
LDAP 事务支持由 提供,这是一种管理 Spring 事务对 LDAP 操作的支持的实现。它与协作者一起跟踪事务中执行的 LDAP 操作,在每次操作之前记录状态,并在事务需要回滚时采取措施恢复初始状态。ContextSourceTransactionManager
PlatformTransactionManager
除了实际的事务管理之外, Spring LDAP 事务支持还确保在同一事务中使用相同的实例。也就是说,在事务完成之前,实际上不会关闭 ,从而可以更高效地使用资源。DirContext
DirContext
虽然 Spring LDAP 用来提供事务支持的方法在许多情况下就足够了,但它绝不是传统意义上的“真正的”事务。 服务器完全不知道事务,因此(例如),如果连接断开,则无法回滚事务。 虽然应该仔细考虑这一点,但也应该注意,另一种选择是在没有任何交易支持的情况下运行。Spring LDAP 的事务支持几乎是最好的。 |
除了原始操作所需的工作之外,客户端事务支持还增加了一些开销。
虽然在大多数情况下,这种开销不应该是一件需要担心的事情,
如果您的应用程序未在同一事务中执行多个 LDAP 操作(例如,后跟 ),
或者,如果不需要与 JDBC 数据源进行事务同步(请参阅 JDBC 事务集成),则使用 LDAP 事务支持几乎没有什么好处。modifyAttributes rebind |
配置
如果您习惯于配置 Spring 事务,那么配置 Spring LDAP 事务应该看起来非常熟悉。您可以使用 、创建实例并在 Bean 配置中包含一个元素来注释事务处理类。以下示例显示了如何执行此操作:@Transactional
TransactionManager
<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 事务,但通过向元素提供属性,支持在概念上将 JDBC 和 LDAP 访问包装在同一事务中。这将创建一个 ,然后,它几乎像管理一个事务一样管理这两个事务。执行提交时,始终首先执行操作的 LDAP 部分,如果 LDAP 提交失败,则允许回滚两个事务。事务的 JDBC 部分的管理与 中 完全相同,只是不支持嵌套事务。以下示例显示了具有 attribute 的元素:data-source-ref
<ldap:transaction-manager>
ContextSourceAndDataSourceTransactionManager
DataSourceTransactionManager
ldap:transaction-manager
data-source-ref
<ldap:transaction-manager data-source-ref="dataSource" >
<ldap:default-renaming-strategy />
<ldap:transaction-manager />
提供的支持都是客户端的。 包装的事务不是 XA 事务。不执行两阶段提交,因为 LDAP 服务器无法对其结果进行投票。 |
你可以通过向元素提供一个属性来完成 Hibernate 集成相同的操作,如下所示:session-factory-ref
<ldap:transaction-manager>
<ldap:transaction-manager session-factory-ref="dataSource" >
<ldap:default-renaming-strategy />
<ldap:transaction-manager />
LDAP 补偿事务说明
Spring LDAP 通过在每次修改操作(、、、、和)之前在 LDAP 树中记录状态来管理补偿事务。
这允许系统在需要回滚事务时执行补偿操作。bind
unbind
rebind
modifyAttributes
rename
在许多情况下,补偿操作非常简单。例如,操作的补偿回滚操作是取消绑定条目。
但是,由于 LDAP 数据库的某些特定特性,其他操作需要不同的、更复杂的方法。
具体来说,并不总是能够获取所有 entry 的值,这使得上述策略不足以进行(例如)操作。bind
Attributes
unbind
这就是为什么在 Spring LDAP 托管事务中执行的每个修改操作在内部都分为四个不同的操作:一个记录操作、 准备操作、提交操作和回滚操作。下表描述了每个 LDAP 操作:
LDAP 操作 | 录音 | 制备 | 犯 | 反转 |
---|---|---|---|---|
|
记录要绑定的条目的 DN。 |
绑定条目。 |
无操作。 |
使用记录的 DN 取消绑定条目。 |
|
记录原始 DN 和目标 DN。 |
重命名该条目。 |
无操作。 |
将条目重命名回其原始 DN。 |
|
记录原始 DN 并计算临时 DN。 |
将条目重命名为临时位置。 |
取消绑定临时条目。 |
将临时位置的条目重命名回其原始 DN。 |
|
记录原始 DN 和新 DN 并计算临时 DN。 |
将条目重命名为临时位置。 |
在原始 DN 处绑定 new,并从其临时位置取消绑定原始条目。 |
将临时位置的条目重命名回其原始 DN。 |
|
记录要修改的条目的 DN 并计算要完成的修改的补偿实例。 |
执行操作。 |
无操作。 |
使用计算的补偿实例执行操作。 |
Javadoc 中提供了 Spring LDAP 事务支持内部工作原理的更详细描述。
重命名策略
如上一节的表格所述,某些操作的事务管理需要暂时重命名受该操作影响的原始条目,然后才能在 commit 中进行实际修改。计算条目的临时 DN 的方式由在配置中声明的子元素中指定的 a 管理。Spring LDAP 包括两种实现:TempEntryRenamingStrategy
<ldap:transaction-manager >
-
DefaultTempEntryRenamingStrategy
(默认):使用元素指定。将后缀添加到条目 DN 的最低有效部分。例如,对于 DN 为 ,此策略返回临时 DN 。您可以通过设置属性来配置后缀。<ldap:default-renaming-strategy />
cn=john doe, ou=users
cn=john doe_temp, ou=users
temp-suffix
-
DifferentSubtreeTempEntryRenamingStrategy
:使用元素指定。它将子树 DN 附加到 DN 的最低重要部分。这样做会使所有临时条目都放置在 LDAP 树中的特定位置。临时子树 DN 是通过设置属性来配置的。例如,如果 is 且条目的原始 DN 为 ,则临时 DN 为 。请注意,配置的 subtree 节点需要出现在 LDAP 树中。<ldap:different-subtree-renaming-strategy />
subtree-node
subtree-node
ou=tempEntries
cn=john doe, ou=users
cn=john doe, ou=tempEntries
在某些情况下不起作用。例如,如果您计划执行递归删除,则需要使用 .这是因为递归删除操作实际上包括单独对子树中每个节点的深度优先删除。由于您无法重命名具有任何子项的条目,并且会将每个节点保留在同一子树中(具有不同的名称),而不是实际删除它,因此此操作将失败。如有疑问,请使用 .DefaultTempEntryRenamingStrategy DifferentSubtreeTempEntryRenamingStrategy DefaultTempEntryRenamingStrategy DifferentSubtreeTempEntryRenamingStrategy |