在不替换整个文档的情况下,一次更新多个对象字段(但其中的一个子集)(Spring 3.1和MongoDB的MongoTemplate)

在不替换整个文档的情况下,一次更新多个对象字段(但其中的一个子集)(Spring 3.1和MongoDB的MongoTemplate),mongodb,spring-3,Mongodb,Spring 3,我试图同时更新“用户”文档中的多个字段。然而,我只想更新一些字段,而不是替换整个文档,而后者似乎是我无法避免的。我使用的方法如下所示: public void mergeUser(User user) { Update mergeUserUpdate = new Update(); mergeUserUpdate.set("firstName", user.getFirstName()); mergeUserUpdate.set("lastName", user.getL

我试图同时更新“用户”文档中的多个字段。然而,我只想更新一些字段,而不是替换整个文档,而后者似乎是我无法避免的。我使用的方法如下所示:

public void mergeUser(User user) {
    Update mergeUserUpdate = new Update();
    mergeUserUpdate.set("firstName", user.getFirstName());
    mergeUserUpdate.set("lastName", user.getLastName());
    mergeUserUpdate.set("username", user.getUsername());

    mongoTemplate.updateFirst(new Query(Criteria.where("_id").is(user.getId())), mergeUserUpdate, User.class);
}
“我的用户”对象确实包含其他字段—
password
字段就是其中之一—但如果在将其设置为某个值之前,立即将其替换为空字符串或完全删除。所以在数据库中,这个:

{
  "_id" : ObjectId("4fc34563c3276c69248271d8"),
  "_class" : "com.test.User",
  "password" : "d26b7f5c0ed888e46889dd1e3d217816d070510596f495e156e9efe4b035fec5a1fe1be643955359",
  "username" : "john@gmail.com",
  "alias" : "john"
}
在调用
mergeUser
方法后,被替换为:

{
  "_id" : ObjectId("4fc34563c3276c69248271d8"),
  "_class" : "com.test.User",
  "username" : "john@gmail.com",
  "firstName" : "John",
  "lastName" : "Doe",
  "address" : {
    "addressLine1" : ""
  }
}
如果我查看更新对象,我会看到它包含以下内容:

{$set={firstName=John, lastName=Doe, username=john@gmail.com}}
在我看来,这是正确的,根据我对MongoDB
$set
函数的理解,这应该只设置指定的值。因此,我希望密码字段保持不变,其他字段也会相应地添加或更改

作为一般性的讨论点,我最终尝试实现某种“合并”功能,Spring将自动神奇地检查所提供的用户对象中存在哪些字段,并仅使用填充的值更新数据库,而不是所有字段。我认为这在理论上是可能的。有人知道这样做的好方法吗

这是我的用户对象,以防万一:

/**
 * Represents an application user.
 */
@Document(collection = "users")
public class User {

    @Id
    private String id;

    @NotEmpty( groups={ChangePasswordValidationGroup.class} )
    private String password;

    @Indexed
    @NotEmpty
    @Email
    private String username;

    private String firstName;

    private String lastName;

    private Date dob;

    private Gender gender;

    private Address address;

    public enum Gender {
        MALE, FEMALE
    }

    // /////// GETTERS AND SETTERS ///////////

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Date getDob() {
        return dob;
    }

    public void setDob(Date dob) {
        this.dob = dob;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getAlias() {
        return alias;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

上面的代码工作得很好。我犯了一个愚蠢的错误,在正确更新后,我再次保存用户对象,并用一个新文档替换它。

有另一种解决方案,可以使用Spring MongoTemplate更新没有某些字段的实体:

DBObject userDBObject = (DBObject) mongoTemplate.getConverter().convertToMongoType(user);
//remove unnecessary fields    
userDBObject.removeField("_id");
userDBObject.removeField("password");

//Create setUpdate & query
Update setUpdate = Update.fromDBObject(new BasicDBObject("$set", userDBObject));
mongoTemplate.updateFirst(new Query(Criteria.where("_id").is(user.getId())), setUpdate , User.class);

//Or use native mongo
//mongoTemplate.getDb().getCollection("user").update(new BasicDBObject("_id",user.getId())
        , new BasicDBObject("$set", userDBObject), false, false);

因为它使用自动转换器,所以当您的实体有许多字段时,它非常有用。

即使这些字段最终没有问题,它也帮助了我。文档显示使用$set操作符保存单个字段,并在保存整个对象时排除$set操作符;它没有提到对整个对象使用$set来保存对象的一部分。现在我知道这是可能的。干杯