对于最新稳定版本,请使用 Spring LDAP 4.0.2!spring-doc.cadn.net.cn

事务支持

程序员从关系型数据库转向LDAP世界时,常常会对LDAP中没有事务概念这一事实感到惊讶。 该协议并未指定事务,也没有LDAP服务器支持它。 为了应对这一重大问题,Spring LDAP为LDAP资源提供了客户端补偿式事务支持。spring-doc.cadn.net.cn

LDAP 事务支持由 ContextSourceTransactionManager 提供,这是 PlatformTransactionManager 的实现,负责管理 LDAP 操作的 Spring 事务支持。与它的合作者们一起,它跟踪事务中执行的 LDAP 操作,记录每个操作前的状态,并在需要回滚事务时采取步骤恢复初始状态。spring-doc.cadn.net.cn

除实际的事务管理外,Spring LDAP 的事务支持还确保在同一事务中使用相同的 DirContext 实例。也就是说,DirContext 直到事务结束才实际关闭,从而实现更高效的资源使用。spring-doc.cadn.net.cn

虽然Spring LDAP提供的事务支持方法在许多情况下是足够的,但绝不是传统意义上的“真正的”事务。 服务器完全不知道这些事务,因此(例如),如果连接中断,就无法回滚事务。 虽然这需要谨慎考虑,但也应该注意到另一种选择是根本不提供任何事务支持。Spring LDAP的事务支持基本上已经尽善尽美了。
客户端事务支持在原有操作所需工作量之外还会增加一些开销。 虽然在大多数情况下,这种开销都不应引起担忧, 但如果您的应用程序并不在同一事务中执行多个LDAP操作(例如,0后跟1), 或者不需要与JDBC数据源进行事务同步(参见JDBC事务集成),使用LDAP事务支持的收益就很小。

配置

配置 Spring LDAP 事务时,如果你习惯于配置 Spring 事务,应该会感到非常熟悉。你可以使用 @Transactional 注解你的事务类,创建一个 TransactionManager 实例,并在你的 bean 配置中包含一个 <tx:annotation-driven> 元素。以下示例展示了如何实现:spring-doc.cadn.net.cn

<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,详情请参阅重命名策略

在实际应用场景中,你可能会在服务对象级别而不是在仓库级别应用事务。前述示例演示了通用的思路。spring-doc.cadn.net.cn

JDBC 事务集成

一个常见的使用场景是在处理LDAP时,部分数据存储在LDAP树中,而其他数据则存储在关系型数据库中。在这种情况下,事务支持变得更加重要,因为不同资源的更新需要同步。spring-doc.cadn.net.cn

虽然实际的XA事务不支持,但通过在<ldap:transaction-manager>元素中为data-source-ref属性提供一个data-source-ref值,可以概念上地将JDBC和LDAP访问包装在同一个事务中,创建一个ContextSourceAndDataSourceTransactionManager,该对象将两个事务虚拟地当作一个来管理。在执行提交时,LDAP部分的操作总是首先进行,若LDAP提交失败则允许两个事务回滚。JDBC部分的事务处理与DataSourceTransactionManager中相同,但不支持嵌套事务。以下示例显示了一个ldap:transaction-manager元素及其data-source-ref属性:spring-doc.cadn.net.cn

<ldap:transaction-manager data-source-ref="dataSource" >
  <ldap:default-renaming-strategy />
<ldap:transaction-manager />
提供的支持均为客户端侧。 包装的事务不是XA事务。不执行两阶段提交,因为LDAP服务器无法对其结果表态。

你可以通过向 <ldap:transaction-manager> 元素提供一个 session-factory-ref 属性来实现与 Hibernate 集成的相同功能,如下所示:spring-doc.cadn.net.cn

<ldap:transaction-manager session-factory-ref="dataSource" >
  <ldap:default-renaming-strategy />
<ldap:transaction-manager />

LDAP 补偿事务解释

Spring LDAP 通过在每次修改操作(0,1,2,3 和 4)之前记录 LDAP 树的状态来管理补偿事务。 这使得系统能够在事务需要回滚时执行补偿操作。spring-doc.cadn.net.cn

在许多情况下,补偿操作很简单。例如,对一个 bind 操作的补偿回滚操作是解绑条目。 其他操作,然而,由于LDAP数据库的某些特定特性,需要采取不同的、更复杂的方法。具体来说,有时无法获取条目中所有 Attributes 的值,使上述策略不足以(例如)处理 unbind 操作。spring-doc.cadn.net.cn

这就是为什么在Spring LDAP管理的事务中执行的每个修改操作在内部都会被拆分为四个不同的操作:记录操作, 准备操作,提交操作和回滚操作。以下表格描述了每个LDAP操作:spring-doc.cadn.net.cn

LDAP 操作 录制 准备 提交 回滚

bindspring-doc.cadn.net.cn

记录条目DN以进行绑定。spring-doc.cadn.net.cn

绑定入口。spring-doc.cadn.net.cn

不操作。spring-doc.cadn.net.cn

使用记录的DN解绑条目。spring-doc.cadn.net.cn

renamespring-doc.cadn.net.cn

记录原始和目标DN。spring-doc.cadn.net.cn

重命名条目。spring-doc.cadn.net.cn

不操作。spring-doc.cadn.net.cn

将条目重命名为其原始DN。spring-doc.cadn.net.cn

unbindspring-doc.cadn.net.cn

记录原始DN并计算临时DN。spring-doc.cadn.net.cn

重命名条目到临时位置。spring-doc.cadn.net.cn

解绑临时条目。spring-doc.cadn.net.cn

将从临时位置重命名的条目重命名为其原始DN。spring-doc.cadn.net.cn

rebindspring-doc.cadn.net.cn

记录原始DN和新的Attributes并计算临时DN。spring-doc.cadn.net.cn

重命名条目到临时位置。spring-doc.cadn.net.cn

将新的 Attributes 绑定到原始 DN,并从其临时位置解除绑定原始条目。spring-doc.cadn.net.cn

将从临时位置重命名的条目重命名为其原始DN。spring-doc.cadn.net.cn

modifyAttributesspring-doc.cadn.net.cn

记录要修改的条目DN,并为将要进行的修改计算补偿ModificationItem个实例。spring-doc.cadn.net.cn

执行 modifyAttributes 操作。spring-doc.cadn.net.cn

不操作。spring-doc.cadn.net.cn

执行一次通过使用计算出的补偿 ModificationItem 实例的 modifyAttributes 操作。spring-doc.cadn.net.cn

关于 Spring LDAP 事务支持的内部工作原理的更详细描述可在 Javadoc 中找到。spring-doc.cadn.net.cn

重命名策略

正如前一节中表格所述,某些操作的事务管理需要在实际修改提交前,先暂时将受影响的条目临时重命名。临时DN的计算方式是由在<ldap:transaction-manager >声明的子元素中指定的TempEntryRenamingStrategy管理。Spring LDAP 包括两个实现:spring-doc.cadn.net.cn

  • DefaultTempEntryRenamingStrategy (默认值): 通过使用一个 <ldap:default-renaming-strategy /> 元素指定。会在条目DN的最不重要部分添加后缀。例如,对于DN为 cn=john doe, ou=users 的情况,此策略会返回一个临时DN为 cn=john doe_temp, ou=users。可以通过设置 temp-suffix 属性来配置后缀。spring-doc.cadn.net.cn

  • DifferentSubtreeTempEntryRenamingStrategy: 通过使用一个<ldap:different-subtree-renaming-strategy />元素指定。它将子树DN追加到DN的最低有效部分。这样会使所有临时条目放置在LDAP树的特定位置。临时子树DN通过设置subtree-node属性配置。例如,如果subtree-nodeou=tempEntries,而条目的原始DN是cn=john doe, ou=users,则临时DN是cn=john doe, ou=tempEntries。需要注意的是,配置的子树节点需要存在于LDAP树中。spring-doc.cadn.net.cn

The DefaultTempEntryRenamingStrategy 在某些情况下不适用。例如,如果你计划进行递归删除,你需要使用 DifferentSubtreeTempEntryRenamingStrategy。这是因为递归删除操作实际上是深度优先地逐个删除子树中的每个节点。由于你不能删除具有子节点的条目,而 DefaultTempEntryRenamingStrategy 会使子树中的每个节点都以不同的名称留在原地,而不是实际删除,因此该操作会失败。不确定时,使用 DifferentSubtreeTempEntryRenamingStrategy