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

Spring Data MongoDB – indexy, anotace a převodníky

1. Přehled

V tomto tutoriálu prozkoumáme některé ze základních funkcí Spring Data MongoDB – indexování, běžné anotace a převodníky.

2. Indexy

2.1. @Indexováno

Tato anotace označí pole jako indexované v MongoDB:

@QueryEntity
@Document
public class User {
    @Indexed
    private String name;
    
    ... 
}

Nyní to jméno pole je indexováno – podívejme se na indexy v prostředí MongoDB:

db.user.getIndexes();

Zde je to, co dostáváme:

[
    {
        "v" : 1,
        "key" : {
             "_id" : 1
         },
        "name" : "_id_",
        "ns" : "test.user"
    }
]

Možná nás překvapí, že po jménu není ani stopa pole kdekoli!

Důvodem je, že od Spring Data MongoDB 3.0 je automatické vytváření indexů ve výchozím nastavení vypnuto .

Toto chování však můžeme změnit explicitním přepsáním autoIndexCreation() metodou v našem MongoConfig :

public class MongoConfig extends AbstractMongoClientConfiguration {

    // rest of the config goes here

    @Override
    protected boolean autoIndexCreation() {
        return true;
    }
}

Pojďme se znovu podívat na indexy v prostředí MongoDB:

[
    {
        "v" : 1,
        "key" : {
             "_id" : 1
         },
        "name" : "_id_",
        "ns" : "test.user"
    },
    {
         "v" : 1,
         "key" : {
             "name" : 1
          },
          "name" : "name",
          "ns" : "test.user"
     }
]

Jak vidíme, tentokrát máme dva indexy – jeden z nich je _id – který byl vytvořen ve výchozím nastavení kvůli @Id anotace a druhé je naše jméno pole.

Alternativně,pokud použijeme Spring Boot, můžeme nastavit spring.data.mongodb.auto-index-creation vlastnost na pravda .

2.2. Vytvořit index programově

Index můžeme vytvořit i programově:

mongoOps.indexOps(User.class).
  ensureIndex(new Index().on("name", Direction.ASC));

Nyní jsme vytvořili index pro pole name a výsledek bude stejný jako v předchozí části.

2.3. Složené indexy

MongoDB podporuje složené indexy, kde jediná struktura indexu obsahuje odkazy na více polí.

Podívejme se na rychlý příklad s použitím složených indexů:

@QueryEntity
@Document
@CompoundIndexes({
    @CompoundIndex(name = "email_age", def = "{'email.id' : 1, 'age': 1}")
})
public class User {
    //
}

Vytvořili jsme složený index s e-mailem a věk pole. Pojďme se nyní podívat na skutečné indexy:

{
    "v" : 1,
    "key" : {
        "email.id" : 1,
        "age" : 1
    },
    "name" : "email_age",
    "ns" : "test.user"
}

Všimněte si, že DBRef pole nelze označit pomocí @Index – toto pole může být pouze součástí složeného indexu.

3. Běžné anotace

3.1. @Transient

Jak bychom očekávali, tato jednoduchá anotace vylučuje zachování pole v databázi:

public class User {
    
    @Transient
    private Integer yearOfBirth;
    // standard getter and setter

}

Vložíme uživatele s polem nastavení yearOfBirth :

User user = new User();
user.setName("Alex");
user.setYearOfBirth(1985);
mongoTemplate.insert(user);

Nyní, když se podíváme na stav databáze, vidíme, že je vyplněno yearOfBirth nebyl uložen:

{
    "_id" : ObjectId("55d8b30f758fd3c9f374499b"),
    "name" : "Alex",
    "age" : null
}

Pokud se tedy zeptáme a zkontrolujeme:

mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getYearOfBirth()

Výsledek bude null .

3.2. @Field

@Field označuje klíč, který se má použít pro pole v dokumentu JSON:

@Field("email")
private EmailAddress emailAddress;

Nyní e-mailová adresa bude uložen do databáze pomocí klíče email:

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

A stav databáze:

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

3.3. @PersistenceConstructor a @Value

@PersistenceConstructor označí konstruktor, dokonce i ten, který je chráněn balíčkem, jako primární konstruktor používaný logikou persistence. Argumenty konstruktoru jsou mapovány podle názvu na hodnoty klíče v načteném DBObject .

Podívejme se na tento konstruktor pro našeho Uživatele třída:

@PersistenceConstructor
public User(String name, @Value("#root.age ?: 0") Integer age, EmailAddress emailAddress) {
    this.name =  name;
    this.age = age;
    this.emailAddress =  emailAddress;
}

Všimněte si použití standardní pružiny @Value anotace zde. S pomocí této anotace můžeme použít jarní výrazy k transformaci hodnoty klíče načtené z databáze předtím, než je použita ke konstrukci doménového objektu. To je zde velmi výkonná a velmi užitečná funkce.

V našem příkladu if věk není nastaveno, bude nastaveno na 0 ve výchozím nastavení.

Pojďme se nyní podívat, jak to funguje:

User user = new User();
user.setName("Alex");
mongoTemplate.insert(user);

Naše databáze bude vypadat:

{
    "_id" : ObjectId("55d074ca0bad45f744a71318"),
    "name" : "Alex",
    "age" : null
}

Tedy věk pole je null , ale když dotazujeme dokument a načteme věk :

mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getAge();

Výsledek bude 0.

4. Konvertory

Pojďme se nyní podívat na další velmi užitečnou funkci v Spring Data MongoDB – převodníky a konkrétně na MongoConverter .

To se používá ke zpracování mapování všech typů Java na DBOobjects při ukládání a dotazování těchto objektů.

Máme dvě možnosti – buď můžeme pracovat s MappingMongoConverter – nebo SimpleMongoConverter v dřívějších verzích (toto bylo v Spring Data MongoDB M3 zastaralé a jeho funkce byly přesunuty do MappingMongoConverter ).

Nebo si můžeme napsat svůj vlastní převodník. K tomu bychom potřebovali implementovat Converter rozhraní a zaregistrujte implementaci v MongoConfig.

Podívejme se na rychlý příklad . Jak jsme viděli v některých výstupech JSON zde, všechny objekty uložené v databázi mají pole _class který se automaticky uloží. Pokud bychom však chtěli toto konkrétní pole během persistence přeskočit, můžeme to udělat pomocí MappingMongoConverter .

Za prvé – zde je implementace vlastního převodníku:

@Component
public class UserWriterConverter implements Converter<User, DBObject> {
    @Override
    public DBObject convert(User user) {
        DBObject dbObject = new BasicDBObject();
        dbObject.put("name", user.getName());
        dbObject.put("age", user.getAge());
        if (user.getEmailAddress() != null) {
            DBObject emailDbObject = new BasicDBObject();
            emailDbObject.put("value", user.getEmailAddress().getValue());
            dbObject.put("email", emailDbObject);
        }
        dbObject.removeField("_class");
        return dbObject;
    }
}

Všimněte si, jak můžeme snadno dosáhnout cíle nepřetrvávat _class konkrétně odstraněním pole přímo zde.

Nyní musíme zaregistrovat vlastní převodník:

private List<Converter<?,?>> converters = new ArrayList<Converter<?,?>>();

@Override
public MongoCustomConversions customConversions() {
    converters.add(new UserWriterConverter());
    return new MongoCustomConversions(converters);
}

Stejného výsledku můžeme samozřejmě dosáhnout také s konfigurací XML, pokud potřebujeme:

<bean id="mongoTemplate" 
  class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongo" ref="mongo"/>
    <constructor-arg ref="mongoConverter" />
    <constructor-arg name="databaseName" value="test"/>
</bean>

<mongo:mapping-converter id="mongoConverter" base-package="org.baeldung.converter">
    <mongo:custom-converters base-package="com.baeldung.converter" />
</mongo:mapping-converter>

Nyní, když uložíme nového uživatele:

User user = new User();
user.setName("Chris");
mongoOps.insert(user);

Výsledný dokument v databázi již neobsahuje informace o třídě:

{
    "_id" : ObjectId("55cf09790bad4394db84b853"),
    "name" : "Chris",
    "age" : null
}

  1. heroku mongohq a mongoid Mongo::ConnectionFailure

  2. Existuje alternativa Javy k Redis, kterou lze vložit?

  3. Chyba Redigo ScanStruct s time.Time

  4. Analýza pomalých dotazů v MongoDB