1。概要
このチュートリアルでは、Spring Data MongoDBのコア機能のいくつか( @DBRef )について引き続き説明します。 注釈とライフサイクルイベント。
2。 @DBRef
マッピングフレームワークは、親子関係の保存をサポートしていません および他のドキュメント内に埋め込まれたドキュメント。ただし、できることは、それらを個別に保存して DBRefを使用できることです。 ドキュメントを参照します。
オブジェクトがMongoDBから読み込まれると、それらの参照は熱心に解決され、マスタードキュメント内に埋め込まれて保存されているかのように見えるマップされたオブジェクトが返されます。
いくつかのコードを見てみましょう:
@DBRef
private EmailAddress emailAddress;
メールアドレス 次のようになります:
@Document
public class EmailAddress {
@Id
private String id;
private String value;
// standard getters and setters
}
マッピングフレームワークはカスケード操作を処理しないことに注意してください 。したがって、たとえば、保存をトリガーした場合 親の場合、子は自動的に保存されません。子も保存する場合は、子の保存を明示的にトリガーする必要があります。
これはまさにライフサイクルイベントが役立つ場所です。 。
3。ライフサイクルイベント
Spring Data MongoDBは、 onBeforeConvert、onBeforeSave、onAfterSave、onAfterLoadなどの非常に便利なライフサイクルイベントを公開しています。 およびonAfterConvert。
イベントの1つをインターセプトするには、 AbstractMappingEventListenerのサブクラスを登録する必要があります。 ここでメソッドの1つをオーバーライドします。イベントがディスパッチされると、リスナーが呼び出され、ドメインオブジェクトが渡されます。
3.1。基本的なカスケード保存
以前の例を見てみましょう–ユーザーを保存します 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 。これらのフィールドが見つかったら、子エンティティを保存します。
FieldCallbackを見てみましょう 子が@Idを持っているかどうかを確認するために使用しているクラス 注釈:
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。カスケードテスト
シナリオを見てみましょう–ユーザーを保存します 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]"
}
}