Spring Data MongoDB - индексы, аннотации и конвертеры

Spring Data MongoDB - индексы, аннотации и конвертеры

1. обзор

В этом руководстве будут рассмотрены некоторые основные функции Spring Data MongoDB - индексация, общие аннотации и конвертеры.

2. Индексы

2.1. @Indexedс

Эта аннотацияmarks the field as indexed в MongoDB:

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

    ...
}

Теперь, когда полеname проиндексировано, давайте посмотрим на индексы в MongoDB:

db.user.getIndexes();

Вот что у нас есть на уровне базы данных:

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

Как видите, у нас есть два индекса - один из них_id - который был создан по умолчанию из-за аннотации@Id иthe second one is our name field.

2.2. Создание индекса программным способом

Мы также можем создать индекс программно:

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

Мы создали индекс для поляname, и результат будет таким же, как и в предыдущем разделе.

2.3. Составные индексы

MongoDB поддерживает составные индексы, где одна структура индекса содержит ссылки на несколько полей.

Давайте посмотрим на быстрый пример использования составных индексов:

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

Мы создали составной индекс с полямиemail иage. Теперь посмотрим на реальные индексы:

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

Обратите внимание, что полеDBRef не может быть помечено@Index - это поле может быть только частью составного индекса.

3. Общие аннотации

3.1 @Transient

Как и следовало ожидать, эта простая аннотация исключает сохранение поля в базе данных:

public class User {

    @Transient
    private Integer yearOfBirth;
    // standard getter and setter

}

Давайте вставим пользователя с полем настройкиyearOfBirth:

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

Теперь, если мы посмотрим на состояние базы данных, мы увидим, что полеyearOfBirth не было сохранено:

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

Так что, если мы запросим и проверим:

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

Результатом будетnull.

3.2. @Fieldс

@Field указывает ключ, который будет использоваться для поля в документе JSON:

@Field("email")
private EmailAddress emailAddress;

ТеперьemailAddress будет сохранен в базе данных с использованием ключаemail:

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

И состояние базы данных:

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

3.3. @PersistenceConstructor и@Value

@PersistenceConstructor отмечает конструктор, даже тот, который защищен пакетом, как основной конструктор, используемый логикой сохранения. Аргументы конструктора сопоставляются по имени с ключевыми значениями в извлеченныхDBObject.

Давайте посмотрим на этот конструктор для нашего классаUser:

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

Обратите внимание на использование здесь стандартной аннотации Spring@Value. Именно с помощью этой аннотации мы можем использовать Spring Expressions для преобразования значения ключа, полученного из базы данных, прежде чем оно будет использовано для создания объекта домена. Это очень мощная и очень полезная функция здесь.

В нашем примере, еслиage не установлен, по умолчанию будет установлено значение0.

Теперь посмотрим, как это работает:

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

Наша база данных будет выглядеть:

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

Итак, полеage - этоnull, но когда мы запрашиваем документ и получаемage:

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

Результат будет 0.

4. Преобразователи

Давайте теперь посмотрим на еще одну очень полезную функцию в Spring Data MongoDB - конвертеры, и в частности наMongoConverter.

Это используется для обработки отображения всех типов Java вDBObjects при хранении и запросе этих объектов.

У нас есть два варианта - мы можем работать либо сMappingMongoConverter –, либо сSimpleMongoConverter в более ранних версиях (это устарело в Spring Data MongoDB M3, а его функциональность была перенесена вMappingMongoConverter).

Или мы можем написать наш собственный конвертер. Для этого нам потребуется реализовать интерфейсConverter и зарегистрировать реализацию вMongoConfig.

Давайте посмотрим наa quick example. Как вы видели в некоторых выходных данных JSON здесь, все объекты, сохраненные в базе данных, имеют поле_class, которое сохраняется автоматически. Однако, если мы хотим пропустить это конкретное поле во время сохранения, мы можем сделать это с помощьюMappingMongoConverter.

Во-первых, вот реализация настраиваемого конвертера:

@Component
public class UserWriterConverter implements Converter {
    @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;
    }
}

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

Теперь нам нужно зарегистрировать пользовательский конвертер:

private List> converters = new ArrayList>();

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

Конечно, мы можем достичь того же результата с помощью конфигурации XML, если нам нужно:


    
    
    



    

Теперь, когда мы сохраняем нового пользователя:

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

Полученный документ в базе данных больше не содержит информацию о классе:

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

5. Заключение

В этом руководстве мы рассмотрели некоторые основные концепции работы с Spring Data MongoDB - индексирование, общие аннотации и преобразователи.

Реализация всех этих примеров и фрагментов кодаcan be found inmy github project - это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.