Обновление полей Specifi c с помощью Spring Data Rest и MongoDB - PullRequest
0 голосов
/ 04 мая 2020

Я использую Spring Data MongoDB и Spring Data Rest для создания REST API, который позволяет выполнять операции GET, POST, PUT и DELETE в моей базе данных MongoDB, и все это работает нормально, за исключением операций обновления (PUT). Это работает, только если я отправляю полный объект в теле запроса.

Например, у меня есть следующая сущность:

@Document
public class User {
    @Id
    private String id;
    private String email;
    private String lastName;
    private String firstName;
    private String password;

    ...
}

Чтобы обновить поле lastName, я должен отправить все объект пользователя, включая пароль! что, очевидно, очень неправильно . Если я отправляю только поле для обновления, все остальные в моей базе данных имеют значение null. Я даже пытался добавить ограничения @NotNull для этих полей, и теперь обновление даже не произойдет, пока я не отправлю все поля объекта пользователя.

Я попытался найти решение здесь, но нашел только следующее опубликовать, но без решения: Как обновить определенное поле в пн go дБ с помощью интерфейса MongoRepository?

Есть ли способ реализовать это?

1 Ответ

1 голос
/ 04 мая 2020

Spring Data Rest использует репозитории Spring Data для автоматического извлечения и обработки постоянных данных с помощью вызовов Rest (см. https://docs.spring.io/spring-data/rest/docs/current/reference/html/#reference).

При использовании Spring Data MongoDB у вас есть * Интерфейс 1006 *, который используется в качестве хранилища для ваших конечных точек отдыха. Однако MongoOperations в настоящее время не поддерживает определенные c обновления полей!

PS: Было бы здорово, если бы они добавили такую ​​функцию, как @DynamicUpdate в Spring Data JPA

Но это не значит, что это можно сделать, вот обходной путь, который я сделал, когда у меня возникла эта проблема.

Сначала позвольте мне объяснить, что мы собираемся сделать:

  1. Мы сделаем создайте контроллер, который будет переопределять все операции PUT, чтобы мы могли реализовать наш собственный метод обновления.
  2. Внутри этого метода обновления мы будем использовать MongoTemplate, у которого есть возможность обновлять определенные c поля.

NB. Мы не хотим повторять эти шаги для каждой модели в нашем приложении, поэтому мы найдем, какую модель обновлять динамически. Для этого мы создадим служебный класс. [Это необязательно]

Давайте начнем с добавления api org.reflections в нашу зависимость проекта, которая позволяет нам получить все классы, которые имеют указанную c аннотацию (@Document в нашем случае):

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.12</version>
</dependency>

Затем создайте новый класс с именем UpdateUtility и добавьте следующие методы, а также замените атрибут MODEL_PACKAGE собственным пакетом, содержащим ваши сущности:

public class UpdateUtility {

    private static final String MODEL_PACKAGE = "com.mycompany.myproject.models";
    private static boolean initialized =  false;
    private static HashMap<String, Class> classContext = new HashMap<>();

    private static void init() {
        if(!initialized) {
            Reflections reflections = new Reflections(MODEL_PACKAGE);
            Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Document.class); // Get all the classes annotated with @Document in the specified package

            for(Class<?> model : classes) {
                classContext.put(model.getSimpleName().toLowerCase(), model);
            }

            initialized = true;
        }
    }

    public static Class getClassFromType(String type) throws Exception{
        init();
        if(classContext.containsKey(type)) {
            return classContext.get(type);
        }
        else {
            throw new Exception("Type " + type + " does not exists !");
        }
    }
}

Используя этот служебный класс, мы можем получить класс модели для обновления из его типа. Например: UpdateUtility.getClassFromType() вернет User.class

Теперь давайте создадим наш контроллер:

public class UpdateController {

    @Autowired
    private MongoTemplate mongoTemplate;

    @PutMapping("/{type}/{id}")
    public Object update(@RequestBody HashMap<String, Object> fields,
                                  @PathVariable(name = "type") String type,
                                  @PathVariable(name = "id") String id) {
        try {
            Class classType = UpdatorUtility.getClassFromType(type); // Get the domain class from the type in the request
            Query query = new Query(Criteria.where("id").is(id)); // Update the document with the given ID
            Update update = new Update();

            // Iterate over the send fields and add them to the update object
            Iterator iterator = fields.entrySet().iterator();
            while(iterator.hasNext()) {
                HashMap.Entry entry = (HashMap.Entry) iterator.next();
                String key = (String) entry.getKey();
                Object value = entry.getValue();
                update.set(key, value);
            }

            mongoTemplate.updateFirst(query, update, classType); // Do the update
            return mongoTemplate.findById(id, classType); // Return the updated document
        } catch (Exception e) {
            // Handle your exception
        }
    }
}

Теперь мы можем обновить указанные поля без изменения вызовов. Так что в вашем случае вызов будет:

PUT http://MY-DOMAIN/user/MY-USER-ID { lastName: "My new last name" }

PS: Вы можете улучшить его, добавив возможность обновлять поле Speci c во вложенных объектах ...

...