使用Spring Data Rest和MongoDB更新特定字段

3

我正在使用Spring Data MongoDB和Spring Data Rest创建一个REST API,允许在我的MongoDB数据库上进行GET、POST、PUT和DELETE操作,并且除了更新(PUT)操作之外,一切正常。只有当我在请求正文中发送完整对象时,它才能正常工作。

例如,我有以下实体:

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

    ...
}

要更新 lastName 字段,我必须发送包括密码在内的整个用户对象,这显然是非常错误的。如果我只发送要更新的字段,则数据库中所有其他字段都将设置为 null。我甚至尝试在这些字段上添加 @NotNull 约束,现在除非我发送用户对象的所有字段,否则更新将无法进行。
我尝试在这里搜索解决方案,但我只找到了以下帖子,但没有解决方案:如何使用 MongoRepository 接口更新 MongoDB 中的特定字段? 是否有一种实现这一点的方法?
1个回答

2
Spring Data Rest使用Spring Data存储库来自动检索和操作持久化数据,使用Rest调用(请查看https://docs.spring.io/spring-data/rest/docs/current/reference/html/#reference)。
使用Spring Data MongoDB时,您有MongoOperations接口,它用作Rest端点的存储库。但是,MongoOperations目前不支持特定字段的更新!
PS:如果像Spring Data JPA中的@DynamicUpdate一样添加此功能将非常棒。
但这并不意味着不能做到,以下是我遇到此问题时所做的解决方法。
首先让我解释一下我们要做什么:
  1. 我们将创建一个控制器,覆盖所有PUT操作,以便我们可以实现自己的更新方法。
  2. 在该更新方法内,我们将使用MongoTemplate,它具有更新特定字段的能力。
注意:我们不想为应用程序中的每个模型重新执行这些步骤,因此我们将动态检索要更新的模型。为了做到这一点,我们将创建一个实用程序类。[这是可选的]
让我们首先将 org.reflections API 添加到项目依赖中,这样我们就可以获取所有具有特定注释(在我们的情况下是 @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:您可以通过添加更新嵌套对象中特定字段的可能性来改进它...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接