トランザクションサポート

LDAP の世界に登場するリレーショナルデータベースの操作に慣れているプログラマーは、トランザクションの概念がないという事実にしばしば驚きを表明します。これはプロトコルで指定されておらず、どの LDAP サーバーもサポートしていません。これが大きな問題になる可能性があることを認識して、Spring LDAP はクライアント側のサポートを提供し、LDAP リソースのトランザクションを補正します。

LDAP トランザクションサポートは、LDAP 操作の Spring トランザクションサポートを管理する PlatformTransactionManager 実装である ContextSourceTransactionManager によって提供されます。コラボレーターとともに、トランザクションで実行された 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://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 トランザクションはサポートされていませんが、<ldap:transaction-manager> 要素に data-source-ref 属性を指定することにより、JDBC および LDAP アクセスを同じトランザクション内に概念的にラップするサポートが提供されています。これにより ContextSourceAndDataSourceTransactionManager が作成され、2 つのトランザクションが仮想的に 1 つのトランザクションであるかのように管理されます。コミットを実行すると、操作の 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 サーバーはその結果に投票できないため、2 フェーズコミットは実行されません。

次のように、<ldap:transaction-manager> 要素に session-factory-ref 属性を指定することで、Hibernate 統合でも同じことを実現できます。

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

LDAP 補正トランザクションの説明

Spring LDAP は、各変更操作 (bindunbindrebindmodifyAttributesrename) の前に LDAP ツリーの状態を記録することにより、補正トランザクションを管理します。これにより、トランザクションをロールバックする必要がある場合に、システムは補正操作を実行できます。

多くの場合、補正操作は非常に簡単です。例: bind 操作の補正ロールバック操作は、エントリのバインドを解除することです。ただし、その他の操作では、LDAP データベースの特定の特性により、別のより複雑なアプローチが必要になります。具体的には、エントリのすべての Attributes の値を常に取得できるとは限らないため、前述の戦略は (たとえば) unbind 操作には不十分です。

これが、Spring LDAP 管理トランザクション内で実行される各変更操作が内部的に 4 つの異なる操作 (記録操作、準備操作、コミット操作、ロールバック操作) に分割される理由です。次の表では、各 LDAP 操作について説明します。

LDAP 操作 記録 準備 コミット ロールバック

bind

バインドするエントリの DN を記録します。

エントリをバインドします。

操作なし。

記録された DN を使用して、エントリのバインドを解除します。

rename

元の DN とターゲット DN を記録します。

エントリの名前を変更します。

操作なし。

エントリの名前を元の DN に戻します。

unbind

元の DN を記録し、一時的な DN を計算します。

エントリの名前を一時的な場所に変更します。

一時エントリのバインドを解除します。

エントリの名前を一時的な場所から元の DN に戻します。

rebind

元の DN と新しい Attributes を記録し、一時的な DN を計算します。

エントリの名前を一時的な場所に変更します。

新しい Attributes を元の DN でバインドし、元のエントリを一時的な場所からバインド解除します。

エントリの名前を一時的な場所から元の DN に戻します。

modifyAttributes

変更するエントリの DN を記録し、変更を行うための補正 ModificationItem インスタンスを計算します。

modifyAttributes 操作を実行します。

操作なし。

計算された補正 ModificationItem インスタンスを使用して、modifyAttributes 操作を実行します。

Spring LDAP トランザクションサポートの内部動作の詳細な説明は、Javadoc にあります。

名前の変更戦略

前のセクションの表で説明したように、一部の操作のトランザクション管理では、コミットで実際の変更を行う前に、操作の影響を受ける元のエントリの名前を一時的に変更する必要があります。エントリの一時 DN が計算される方法は、構成の <ldap:transaction-manager > 宣言の子要素で指定された TempEntryRenamingStrategy によって管理されます。Spring LDAP には、次の 2 つの実装が含まれています。

  • DefaultTempEntryRenamingStrategy (デフォルト): <ldap:default-renaming-strategy /> 要素を使用して指定します。エントリ DN の最下位部分にサフィックスを追加します。例: cn=john doe, ou=users の DN の場合、この戦略は cn=john doe_temp, ou=users の一時的な DN を返します。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 を使用してください。