sql >> Databáze >  >> NoSQL >> MongoDB

Vlastní kaskádování v Spring Data MongoDB

1. Přehled

Tento tutoriál bude pokračovat ve zkoumání některých základních funkcí Spring Data MongoDB – @DBRef anotace a události životního cyklu.

2. @DBRef

Rámec mapování nepodporuje ukládání vztahů rodič-dítě a vložené dokumenty do jiných dokumentů. Co však můžeme udělat, je – můžeme je uložit samostatně a použít DBRef odkazovat na dokumenty.

Když je objekt načten z MongoDB, budou tyto odkazy dychtivě vyřešeny a my dostaneme zpět namapovaný objekt, který vypadá stejně, jako by byl uložen jako vložený do našeho hlavního dokumentu.

Podívejme se na nějaký kód:

@DBRef
private EmailAddress emailAddress;

E-mailová adresa vypadá takto:

@Document
public class EmailAddress {
    @Id
    private String id;
    
    private String value;
    
    // standard getters and setters
}

Všimněte si, že mapovací rámec nezpracovává kaskádové operace . Tedy – například – když spustíme uložení na rodiči se dítě neuloží automaticky – pokud jej chceme uložit také, budeme muset uložení u dítěte výslovně spustit.

Právě zde se události životního cyklu hodí .

3. Události životního cyklu

Spring Data MongoDB publikuje některé velmi užitečné události životního cyklu – například onBeforeConvert, onBeforeSave, onAfterSave, onAfterLoad a onAfterConvert.

Abychom mohli zachytit jednu z událostí, musíme zaregistrovat podtřídu AbstractMappingEventListener a přepište jednu z metod zde. Když je událost odeslána, bude zavolán náš posluchač a předán objekt domény.

3.1. Základní Cascade Save

Podívejme se na příklad, který jsme měli dříve – uložení uživatele s e-mailovou adresou . Nyní můžeme poslouchat onBeforeConvert událost, která bude volána předtím, než doménový objekt přejde do konvertoru:

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());
        }
    }
}

Nyní zbývá pouze zaregistrovat posluchače do MongoConfig :

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

Nebo jako XML:

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

A máme hotovou kaskádovou sémantiku – i když pouze pro uživatele.

3.2. Obecná kaskádová implementace

Pojďme nyní vylepšit předchozí řešení tím, žezobecníme funkci kaskády. Začněme definováním vlastní anotace:

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

Pojďme nyní zapracovat na našem vlastním posluchači abyste s těmito poli zacházeli obecně a nemuseli je přetypovat na žádnou konkrétní entitu:

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));
    }
}

Od jara tedy používáme nástroj reflexe a naše zpětné volání spouštíme ve všech polích, která splňují naše kritéria:

@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);
        }
    }
}

Jak můžete vidět, hledáme pole, která mají oba DBRef anotace a také CascadeSave . Jakmile tato pole najdeme, podřízenou entitu uložíme.

Podívejme se na FieldCallback třídu, kterou používáme ke kontrole, zda má dítě @Id anotace:

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;
    }
}

Nakonec, aby to všechno fungovalo dohromady, musíme samozřejmě emailAddress pole, které má být nyní správně anotováno:

@DBRef
@CascadeSave
private EmailAddress emailAddress;

3.3. Kaskádový test

Pojďme se nyní podívat na scénář – uložíme Uživatele s e-mailovou adresou a operace ukládání automaticky přechází do této vložené entity:

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

Pojďme zkontrolovat naši databázi:

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

  1. Použití Redis pro fronty pro více aplikací Laravel na jednom serveru

  2. Seskupte a počítejte s podmínkou

  3. JSON serializující Mongodb

  4. Běží mongodb?