【问题标题】:Update Specific Fields with Spring Data Rest and MongoDB使用 Spring Data Rest 和 MongoDB 更新特定字段
【发布时间】:2020-05-04 12:51:42
【问题描述】:

我正在使用 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 字段,我必须发送所有用户对象,包括密码!这显然是非常错误的。 如果我只发送要更新的字段,那么我的数据库中的所有其他字段都设置为空。我什至尝试在这些字段上添加@NotNull 约束,现在更新甚至不会发生,除非我发送所有用户对象的字段。

我尝试在这里寻找解决方案,但我只找到以下帖子但没有解决方案:How to update particular field in mongo db by using MongoRepository Interface?

有没有办法实现这个?

【问题讨论】:

    标签: java spring mongodb spring-data-mongodb spring-data-rest


    【解决方案1】:

    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:您可以通过添加更新嵌套对象中特定字段的可能性来改进它...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-14
      • 1970-01-01
      • 2018-03-31
      • 2019-01-23
      • 2017-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-13
      相关资源
      最近更新 更多