Spring Data MongoDBでのカスタムカスケード

データ]

  • リンク:/tag/mongodb/[MongoDB]

1概要

このチュートリアルでは、Spring Data MongoDBのいくつかのコア機能、つまり@ @ DBRef__アノテーションとライフサイクルイベントについて引き続き説明します。

2 @ DBRef

マッピングフレームワークは、 親子関係の保存 や他のドキュメント内の埋め込みドキュメントをサポートしていません。私たちができることは何ですか - 私たちはそれらを別々に保存し、ドキュメントを参照するために DBRef を使うことができます。

オブジェクトがMongoDBからロードされると、それらの参照は熱心に解決され、マスタードキュメントに埋め込まれて格納されている場合と同じように見えるマップオブジェクトが返されます。

いくつかのコードを見てみましょう。

@DBRef
private EmailAddress emailAddress;

EmailAddress は次のようになります。

@Document
public class EmailAddress {
    @Id
    private String id;

    private String value;

   //standard getters and setters
}

マッピングフレームワークはカスケード操作を処理しないことに注意してください。

そのため、たとえば、親で save をトリガーしても、子は自動的に保存されません。保存したい場合は、子への保存も明示的にトリガーする必要があります。

これはまさにライフサイクルイベントが役立つところです。

3ライフサイクルイベント

Spring Data MongoDBは、 onBeforeConvert、onBeforeSave、onAfterSave、onAfterLoad onAfterConvert. など、非常に便利なライフサイクルイベントを公開しています。

いずれかのイベントをインターセプトするには、 AbstractMappingEventListener のサブクラスを登録し、ここでいずれかのメソッドをオーバーライドする必要があります。

イベントが送出されると、リスナーが呼び出され、ドメインオブジェクトが渡されます。

3.1. 基本カスケード保存

先ほどの例を見てみましょう - user emailAddress で保存します。ドメインオブジェクトがコンバーターに入る前に呼び出される onBeforeConvert イベントを聞くことができます。

public class UserCascadeSaveMongoEventListener extends AbstractMongoEventListener<Object> {
    @Autowired
    private MongoOperations mongoOperations;

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Object> event) {
        Object source = event.getSource();
        if ((source instanceof User) && (((User) source).getEmailAddress() != null)) {
            mongoOperations.save(((User) source).getEmailAddress());
        }
    }
}

それでは、リスナーを MongoConfig に登録するだけです。

@Bean
public UserCascadeSaveMongoEventListener userCascadingMongoEventListener() {
    return new UserCascadeSaveMongoEventListener();
}

またはXMLとして:

<bean class="org.baeldung.event.UserCascadeSaveMongoEventListener"/>

そして、私たちはカスケードセマンティクスをすべて終えました - ユーザーのためだけですが。

3.2. 一般的なカスケード実装

カスケード機能を汎用にすることで、以前のソリューションを改善しましょう。** カスタム注釈を定義することから始めましょう。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CascadeSave {
   //}

それでは、これらのフィールドを総称的に処理し、特定のエンティティにキャストする必要がないようにしましょう。

public class CascadeSaveMongoEventListener extends AbstractMongoEventListener<Object> {

    @Autowired
    private MongoOperations mongoOperations;

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Object> event) {
        Object source = event.getSource();
        ReflectionUtils.doWithFields(source.getClass(),
          new CascadeCallback(source, mongoOperations));
    }
}

つまり、Springではリフレクションユーティリティを使用しており、基準を満たすすべてのフィールドでコールバックを実行しています。

@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
    ReflectionUtils.makeAccessible(field);

    if (field.isAnnotationPresent(DBRef.class) &&
      field.isAnnotationPresent(CascadeSave.class)) {

        Object fieldValue = field.get(getSource());
        if (fieldValue != null) {
            FieldCallback callback = new FieldCallback();
            ReflectionUtils.doWithFields(fieldValue.getClass(), callback);

            getMongoOperations().save(fieldValue);
        }
    }
}

ご覧のとおり、 DBRef アノテーションと CascadeSave の両方を持つフィールドを探しています。これらのフィールドを見つけたら、子エンティティを保存します。

子に @ Id アノテーションがあるかどうかを確認するために使用している FieldCallback クラスを見てみましょう。

public class FieldCallback implements ReflectionUtils.FieldCallback {
    private boolean idFound;

    public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
        ReflectionUtils.makeAccessible(field);

        if (field.isAnnotationPresent(Id.class)) {
            idFound = true;
        }
    }

    public boolean isIdFound() {
        return idFound;
    }
}

最後に、それをすべて一緒に機能させるには、もちろん、正しく注釈を付けるために emailAddress フィールドが必要です。

@DBRef
@CascadeSave
private EmailAddress emailAddress;

3.3. カスケードテスト

シナリオを見てみましょう - User emailAddress で保存すると、保存操作がこの埋め込みエンティティに自動的にカスケードされます。

User user = new User();
user.setName("Brendan");
EmailAddress emailAddress = new EmailAddress();
emailAddress.setValue("[email protected]");
user.setEmailAddress(emailAddress);
mongoTemplate.insert(user);

データベースを確認しましょう。

{
    "__id" : ObjectId("55cee9cc0badb9271768c8b9"),
    "name" : "Brendan",
    "age" : null,
    "email" : {
        "value" : "[email protected]"
    }
}

4結論

この記事では、Spring Data MongoDBのいくつかの優れた機能( @ DBRef アノテーション、ライフサイクルイベント、およびカスケードをインテリジェントに処理する方法)について説明しました。

これらすべての例とコードスニペットの実装は、https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-mongodb[over on GitHub]** にあります。プロジェクトなので、そのままインポートして実行するのは簡単なはずです。

前の投稿:Java String.replaceAll()
次の投稿:春豆にモッキートモックを注入する