MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Персонализирано каскадиране в Spring Data MongoDB

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.

За да прихванем едно от събитията, трябва да регистрираме подклас на AbstractMappingEventListener и отменете един от методите тук. Когато събитието бъде изпратено, нашият слушател ще бъде извикан и обектът на домейна ще бъде предаден.

3.1. Basic Cascade Save

Нека разгледаме примера, който имахме по-рано – запазване на потребител с имейл адрес . Вече можем да слушаме 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]"
    }
}

  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Високопроизводителни MongoDB клъстери на Amazon EC2

  2. Поддокументи на Mongoose срещу вложена схема

  3. Проектирайте първия елемент в масива към ново поле (обединяване на MongoDB)

  4. Как да използвате MongoDB Connection Pooling на AWS Lambda

  5. MongoDB директория с данни /data/db не е намерена