Transaction Support
习惯于使用关系数据库的程序员在进入 LDAP 领域时常常会对没有事务这一概念感到惊讶。协议中未指定该概念,没有 LDAP 服务器支持它。Spring LDAP 认识到这可能是一个主要问题,因此为 LDAP 资源中的客户端补偿事务提供了支持。
Programmers used to working with relational databases coming to the LDAP world often express surprise at the fact that there is no notion of transactions. It is not specified in the protocol, and no LDAP servers support it. Recognizing that this may be a major problem, Spring LDAP provides support for client-side, compensating transactions on LDAP resources.
LDAP 事务支持由 ContextSourceTransactionManager
提供,它是一个 PlatformTransactionManager
实现,管理 LDAP 操作的 Spring 事务支持。它与其协作者一起跟踪事务中执行的 LDAP 操作,记录每个操作之前的状态,并采取措施在需要回滚事务时恢复初始状态。
LDAP transaction support is provided by ContextSourceTransactionManager
, a PlatformTransactionManager
implementation that manages Spring transaction support for LDAP operations. Along with its collaborators, it keeps track of the LDAP operations performed in a transaction, making a record of the state before each operation and taking steps to restore the initial state should the transaction need to be rolled back.
除了实际的事务管理之外,Spring LDAP 事务支持还确保在整个事务期间使用相同的 DirContext
实例。也就是说,在事务完成之前实际上不会关闭 DirContext
,从而可以更有效地使用资源。
In addition to the actual transaction management, Spring LDAP transaction support also makes sure that the same DirContext
instance is used throughout the same transaction. That is, the DirContext
is not actually closed until the transaction is finished, allowing for more efficient resources usage.
尽管 Spring LDAP 提供的事务支持适用于许多情况,但它绝不是传统意义上的 “real” 事务。服务器完全不感知事务,所以(例如)如果连接断开,就无法回滚该事务。虽然需要仔细考虑这一点,还应注意,替代方法是在没有任何事务支持的情况下进行操作。Spring LDAP 的事务支持几乎是尽善尽美了。
While the approach used by Spring LDAP to provide transaction support is sufficient for many cases, it is by no means “real” transactions in the traditional sense. The server is completely unaware of the transactions, so (for example), if the connection is broken, there is no way to roll back the transaction. While this should be carefully considered, it should also be noted that the alternative is to operate without any transaction support whatsoever. Spring LDAP’s transaction support is pretty much as good as it gets.
客户端事务支持除了原始操作所需的工作之外,还增加了额外的开销。虽然大多数情况下不用担心这种开销,如果您的应用程序不执行同一事务中的多个 LDAP 操作(例如,执行 |
The client-side transaction support adds some overhead in addition to the work required by the original operations.
While this overhead should not be something to worry about in most cases,
if your application does not perform several LDAP operations within the same transaction (for example, |
Configuration
如果已经习惯了配置 Spring 事务,那么配置 Spring LDAP 事务应该是非常熟悉的。您可以使用 @Transactional
为您的事务类添加注释,创建一个 TransactionManager
实例,并在 Bean 配置中包含一个 <tx:annotation-driven>
元素。以下示例展示了如何执行此操作:
Configuring Spring LDAP transactions should look very familiar if you are used to configuring Spring transactions. You can annotate your transacted classes with @Transactional
, create a TransactionManager
instance, and include a <tx:annotation-driven>
element in your bean configuration. The following example shows how to do so:
<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 />
...
虽然此设置适用于大多数简单用例,一些更复杂的方案需要额外的配置。具体来说,如果您需要在事务中创建或删除子树,则需要使用替代的 |
While this setup works fine for most simple use cases, some more complex scenarios require additional configuration.
Specifically, if you need to create or delete subtrees within transactions, you need to use an alternative |
在实际情况中,您可能更愿意在服务对象级别而非存储库级别应用事务。之前的示例展示了这个基本思想。
In a real-world situation, you would probably apply the transactions on the service-object level rather than the repository level. The preceding example demonstrates the general idea.
JDBC Transaction Integration
使用 LDAP 工作时的常见用例是,部分数据存储在 LDAP 树中,但其他数据存储在关系数据库中。在这种情况下,因为不同资源更新应该同步,事务支持变得更加重要。
A common use case when working against LDAP is that some of the data is stored in the LDAP tree but other data is stored in a relational database. In this case, transaction support becomes even more important, since the update of the different resources should be synchronized.
尽管不支持实际的 XA 事务,但可以提供支持,通过向 <ldap:transaction-manager>
元素提供 data-source-ref
属性来在同一个事务中概念性地包装 JDBC 和 LDAP 访问。这会创建一个 ContextSourceAndDataSourceTransactionManager
,然后它会像一个事务一样虚拟地管理两个事务。在执行提交时,操作的 LDAP 部分将始终首先执行,允许在 LDAP 提交失败时回滚这两个事务。事务的 JDBC 部分将被管理,就像在 DataSourceTransactionManager
中一样,只是不支持嵌套事务。下面的示例显示了一个具有 data-source-ref
属性的 ldap:transaction-manager
元素:
While actual XA transactions is not supported, support is provided to conceptually wrap JDBC and LDAP access within the same transaction by supplying a data-source-ref
attribute to the <ldap:transaction-manager>
element. This creates a ContextSourceAndDataSourceTransactionManager
, which then manages the two transactions virtually as if they were one. When performing a commit, the LDAP part of the operation is always performed first, letting both transactions be rolled back should the LDAP commit fail. The JDBC part of the transaction is managed exactly as in DataSourceTransactionManager
, except that nested transactions are not supported. The following example shows an ldap:transaction-manager
element with a data-source-ref
attribute:
<ldap:transaction-manager data-source-ref="dataSource" >
<ldap:default-renaming-strategy />
<ldap:transaction-manager />
提供的支持全是客户端的。打包的事务不是 XA 事务。不执行两阶段提交,因为 LDAP 服务器无法对提交结果表决。 |
The provided support is all client-side. The wrapped transaction is not an XA transaction. No two-phase commit is performed, as the LDAP server cannot vote on its outcome. |
您可以通过下列方法,为 Hibernate 集成执行相同操作:向 <ldap:transaction-manager>
元素提供 session-factory-ref
属性:
You can accomplish the same thing for Hibernate integration by supplying a session-factory-ref
attribute to the <ldap:transaction-manager>
element, as follows:
<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 树状态,从而管理补偿性事务。如果需要回滚事务,这会让系统执行补偿性操作。
Spring LDAP manages compensating transactions by making a record of the state in the LDAP tree before each modifying operation (bind
, unbind
, rebind
, modifyAttributes
, and rename
).
This lets the system perform compensating operations should the transaction need to be rolled back.
在很多情况下,补偿性操作非常简单。例如,bind
操作的补偿性回滚操作是取消绑定条目。但是,由于 LDAP 数据库的某些特定特征,其他操作需要采用不同的、更为复杂的方法。具体来说,并非总能获得条目的所有 Attributes
的值,这使得前述策略不够用(例如)unbind
操作。
In many cases, the compensating operation is pretty straightforward. For example, the compensating rollback operation for a bind
operation is to unbind the entry.
Other operations, however, require a different, more complicated approach because of some particular characteristics of LDAP databases.
Specifically, it is not always possible to get the values of all Attributes
of an entry, making the aforementioned strategy insufficient for (for example) an unbind
operation.
这就是在 Spring LDAP 管理的事务中执行的每个修改操作在内部都被分成四个不同操作的原因:一个记录操作、一个准备操作、一个提交操作和一个回滚操作。下表描述了每个 LDAP 操作:
This is why each modifying operation performed within a Spring LDAP managed transaction is internally split up into four distinct operations: a recording operation, a preparation operation, a commit operation, and a rollback operation. The following table describes each LDAP operation:
LDAP Operation | Recording | Preparation | Commit | Rollback |
---|---|---|---|---|
|
Make a record of the DN of the entry to bind. |
Bind the entry. |
No operation. |
Unbind the entry by using the recorded DN. |
|
Make a record of the original and target DN. |
Rename the entry. |
No operation. |
Rename the entry back to its original DN. |
|
Make a record of the original DN and calculate a temporary DN. |
Rename the entry to the temporary location. |
Unbind the temporary entry. |
Rename the entry from the temporary location back to its original DN. |
|
Make a record of the original DN and the new |
Rename the entry to a temporary location. |
Bind the new |
Rename the entry from the temporary location back to its original DN. |
|
Make a record of the DN of the entry to modify and calculate compensating |
Perform the |
No operation. |
Perform a |
Spring LDAP 事务支持的内部工作机制的更详细说明在 Javadoc 中提供。
A more detailed description of the internal workings of the Spring LDAP transaction support is available in the Javadoc.
Renaming Strategies
如前一节中的表格所述,有些操作的事务管理需要在提交中可以进行实际修改之前,临时重命名该操作影响的原始条目。计算条目的临时 DN 的方式由配置中 <ldap:transaction-manager >
声明的子元素中指定的 TempEntryRenamingStrategy
管理。Spring LDAP 包含两个实现:
As described in the table in the preceding section, the transaction management of some operations requires the original entry affected by the operation to be temporarily renamed before the actual modification can be made in the commit. The manner in which the temporary DN of the entry is calculated is managed by a TempEntryRenamingStrategy
that is specified in a child element of the <ldap:transaction-manager >
declaration in the configuration. Spring LDAP includes two implementations:
-
DefaultTempEntryRenamingStrategy
(the default): Specified by using an<ldap:default-renaming-strategy />
element. Adds a suffix to the least significant part of the entry DN. For example, for a DN ofcn=john doe, ou=users
, this strategy returns a temporary DN ofcn=john doe_temp, ou=users
. You can configure the suffix by setting thetemp-suffix
attribute. -
DifferentSubtreeTempEntryRenamingStrategy
: Specified by using an<ldap:different-subtree-renaming-strategy />
element. It appends a subtree DN to the least significant part of the DN. Doing so makes all temporary entries be placed at a specific location in the LDAP tree. The temporary subtree DN is configured by setting thesubtree-node
attribute. For example, ifsubtree-node
isou=tempEntries
and the original DN of the entry iscn=john doe, ou=users
, the temporary DN iscn=john doe, ou=tempEntries
. Note that the configured subtree node needs to be present in the LDAP tree.
|
The |