Рубрики
Без рубрики

Пользовательские Каскадирование в весенних данных MongoDB

Как реализовать прочный, общий каскадный механизм с событиями жизненного цикла в Spring Data MongoDB.

Автор оригинала: baeldung.

1. Обзор

Этот учебник будет продолжать изучать некоторые из основных особенностей Весенние данные 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
}

Обратите внимание, что структура отображения не справляется с каскадными операциями . Так , например, если мы запускаем сохранить на родителе, ребенок не будет сохранен автоматически – мы должны явно вызвать сохранить на ребенка, если мы хотим сохранить его, а также.

Это именно то, где события жизненного цикла пригодятся .

3. События жизненного цикла

Spring Data MongoDB публикует некоторые очень полезные события жизненного цикла , такие как onBeforeConvert, onBeforeSave, onAfterSave, onAfterLoad и наАфтерКонверт.

Чтобы перехватить одно из событий, нам нужно зарегистрировать подкласс АбстрактНаппингEventListener и переопределить один из методов здесь. Когда событие будет отправлено, наш слушатель будет вызван и доменный объект будет передан дюйма

3.1. Основные Каскад Сохранить

Давайте посмотрим на пример, который мы имели ранее – сохранение пользовательские с электронная почтаАдрес . Теперь мы можем слушать onBeforeConvert событие, которое будет вызвано до того, как объект домена переходит в преобразователь:

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

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

Теперь нам просто нужно зарегистрировать слушателя в МонгоКонфиг :

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

Или как XML:

И у нас есть каскадные семантики все сделано – хотя и только для пользователя.

3.2. Общий каскадный проект

Давайте теперь улучшить предыдущее решение, что делает функциональность каскада общей. Начнем с определения пользовательской аннотации:

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

Давайте теперь работать над нашим пользовательским слушателем для обработки этих полей в общем и не должны отливок для какой-либо конкретной сущности:

public class CascadeSaveMongoEventListener extends AbstractMongoEventListener {

    @Autowired
    private MongoOperations mongoOperations;

    @Override
    public void onBeforeConvert(BeforeConvertEvent 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 аннотация, а также КаскадСейв . Как только мы найдем эти поля, мы спасем сущность ребенка.

Давайте посмотрим на ФилдКаллбэк класс, который мы используем, чтобы проверить, если ребенок имеет @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;
    }
}

Наконец, чтобы все это сработало вместе, нам, конечно, нужно электронная почтаАдрес поле, чтобы теперь быть правильно аннотированы:

@DBRef
@CascadeSave
private EmailAddress emailAddress;

3.3. Каскадный тест

Давайте теперь посмотрим на сценарий – мы экономим Пользователь с электронная почтаАдрес , и каскады операции сохранения к этой встроенной сущности автоматически:

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 аннотация, события жизненного цикла и как мы можем справиться с каскадированием разумно.

Реализация всех этих примеров и фрагментов кода можно найти более на GitHub – это проект на основе Maven, поэтому он должен быть легким для импорта и запуска, как она есть.