MongoTemplate upsert-простой способ сделать обновление из pojo (какой пользователь редактировал)?

вот простой pojo:

public class Description {
    private String code;
    private String name;
    private String norwegian;
    private String english;
}

и, пожалуйста, см. следующий код, чтобы применить upsert в MongoDb через весенний MongoTemplate:

Query query = new Query(Criteria.where("code").is(description.getCode()));
Update update = new Update().set("name", description.getName()).set("norwegian", description.getNorwegian()).set("english", description.getEnglish());
mongoTemplate.upsert(query, update, "descriptions");

строка для создания Update объект указывает каждое поле Item класса вручную.

но если мой Item объект изменяется, затем мой слой Dao ломается.

так есть способ избежать этого, чтобы все поля из моего Item class применяются автоматически к обновление?

Э. Г.

Update update = new Update().fromObject(item);

обратите внимание, что мой pojo не распространяется DBObject.

10 ответов


я столкнулся с такой же проблемой. В версии Het current Spring Data MongoDB нет такой вещи. Вы должны вручную обновить отдельные поля.

однако это возможно с другой структурой: морфия.

эта система имеет обертку для функции Дао: https://github.com/mongodb/morphia/wiki/DAOSupport

вы можете использовать API DAO для таких вещей:

SomePojo pojo = daoInstance.findOne("some-field", "some-value");
pojo.setAProperty("changing this property");
daoInstance.save(pojo);

Я нашел очень хорошее решение для этого вопроса

//make a new description here
Description d = new Description();
d.setCode("no");
d.setName("norwegian");
d.setNorwegian("norwegian");
d.setEnglish("english");

//build query
Query query = new Query(Criteria.where("code").is(description.getCode()));

//build update
DBObject dbDoc = new BasicDBObject();
mongoTemplate.getConverter().write(d, dbDoc); //it is the one spring use for convertions.
Update update = Update.fromDBObject(dbDoc);

//run it!
mongoTemplate.upsert(query, update, "descriptions");

Plz обратите внимание на это обновление.fromdbobject возвращает объект обновления со всеми полями в dbDoc. Если вы просто хотите обновить ненулевые поля, вы должны закодировать новый метод для исключения нулевых полей.

например, front-end post a doc, как показано ниже:

//make a new description here
Description d = new Description();
d.setCode("no");
d.setEnglish("norwegian");

нам нужно только обновить поле "язык":

//return Update object
public static Update fromDBObjectExcludeNullFields(DBObject object) {
    Update update = new Update();       
    for (String key : object.keySet()) {
        Object value = object.get(key);
        if(value!=null){
            update.set(key, value);
        }
    }
    return update;
}

//build udpate
Update update = fromDBObjectExcludeNullFields(dbDoc);

вы можете использовать save : (если не существует = вставить else = upsert)

save (Object objectToSave, String collectionName)

читать : javadoc


это то, что я делаю в настоящее время. Не так много элегантный способ сделать это, но он сохраняет драгоценный вызов DB:

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;

/**
 * Perform an upsert operation to update ALL FIELDS in an object using native mongo driver's methods
 * since mongoTemplate's upsert method doesn't allow it
 * @param upsertQuery
 * @param object
 * @param collectionName
 */
private void performUpsert(Query upsertQuery, Object object, String collectionName){

    ObjectMapper mapper = new ObjectMapper();

    try {
        String jsonStr = mapper.writeValueAsString(object);
        DB db = mongoTemplate.getDb();
        DBCollection collection = db.getCollection(collectionName);
        DBObject query = upsertQuery.getQueryObject();
        DBObject update = new BasicDBObject("$set", JSON.parse(jsonStr));
        collection.update(query, update, true, false);
    } catch (IOException e) {
        LOGGER.error("Unable to persist the metrics in DB. Error while parsing object: {}", e);
    }
}

если вы хотите upsert Pojos ВКЛ. собственность String id; вы должны исключить поле _id в методе fromDBObject Update.fromDBObject(dbDoc,"_id").

в противном случае вы получите исключение:

org.springframework.dao.DuplicateKeyException: { "serverUsed" : "127.0.0.1:27017" , "ok" : 1 , "n" : 0 , "updatedExisting" : false , "err" : "E11000 duplicate key error collection: db.description index: _id_ dup key: { : null }" , "code" : 11000}; nested exception is com.mongodb.MongoException$DuplicateKey: { "serverUsed" : "127.0.0.1:27017" , "ok" : 1 , "n" : 0 , "updatedExisting" : false , "err" : "E11000 duplicate key error collection: db.description index: _id_ dup key: { : null }" , "code" : 11000}

потому что поле _id первого равно null

{
    "_id" : null,
...

}

Fullcode на основе @PaniniGelato ответ будет

public class Description(){
    public String id;
...
}

Description d = new Description();
d.setCode("no");
d.setName("norwegian");
d.setNorwegian("norwegian");
d.setEnglish("english");

//build query
Query query = new Query(Criteria.where("code").is(description.getCode()));

//build update
DBObject dbDoc = new BasicDBObject();
mongoTemplate.getConverter().write(d, dbDoc); //it is the one spring use for convertions.
Update update = Update.fromDBObject(dbDoc, "_id");

//run it!
mongoTemplate.upsert(query, update, "descriptions");

тогда upsert работает в случаях insert и обновление. Исправления и мысли приветствуются ;)


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

  1. обновить элемент, который ранее был извлечен из БД.
  2. Update или Insert (вставка) элемент создается кодом.

в случае 1) Вы можете просто использовать mongoTemplate.save (pojo, "коллекция"), потому что ваш POJO уже будет иметь заполненный ObjectID в поле id.

в случае 2) вы должны объяснить монго, что означает "уже существует" в случае ваш домен model: по умолчанию the mongoTemplate.метод save () обновляет существующий элемент, если он имеет тот же ObjectId. Но с недавно созданным POJO у вас нет этого идентификатора. Поэтому mongoTemplate.метод upsert () имеет параметр запроса, который можно создать следующим образом:

MyDomainClass pojo = new MyDomainClass(...);

Query query = Query.query(Criteria.where("email").is("user1@domain.com"));

DBObject dbDoc = new BasicDBObject();
mongoTemplate.getConverter().write(pojo, dbDoc);   //it is the one spring use for convertions.
dbDoc.removeField("_id");    // just to be sure to not create any duplicates
Update update = Update.fromDBObject(dbDoc);

WriteResult writeResult = mongoTemplate.upsert(query, update, UserModel.class);

Я думаю, что: Описание добавить свойство

@Id
private String id;

затем получите документ по условию запроса, установите идентификатор описания по идентификатору документа. и сохранить


просто использовать ReflectionDBObject - Если вы сделаете Description расширьте его, вы должны просто передать поля вашего объекта в Update рефлекторно, автоматически. Примечание выше о нулевых полях, включенных в обновление, по-прежнему остается верным.


public void saveOrUpdate(String json) {
    try {
        JSONObject jsonObject = new JSONObject(json);
        DBObject update1 = new BasicDBObject("$set", JSON.parse(json));
        mongoTemplate.getCollection("collectionName").update(new Query(Criteria.where("name").is(jsonObject.getString("name"))).getQueryObject(), update1, true, false);
    } catch (Exception e) {
        throw new GenericServiceException("Error while save/udpate. Error msg: " + e.getMessage(), e);
    }

}

это очень простой способ сохранить строку json в коллекцию с помощью mongodb и весна. Этот метод можно переопределить для использования в качестве JSONObject.


решение для новой spring-data-mongodb версии 2.Х. Х.

API эволюционировал, начиная с 2.Х. Х версии тут:

Update.fromDocument(org.bson.Document object, String... exclude)

вместо (1.Х. Х):

Update.fromDBObject(com.mongodb.DBObject object, String... exclude)

решение:

//make a new description here
Description d = new Description();
d.setCode("no");
d.setName("norwegian");
d.setNorwegian("norwegian");
d.setEnglish("english");
Query query = new Query(Criteria.where("code").is(description.getCode()));

Document doc = new Document(); // org.bson.Document
mongoTemplate.getConverter().write(item, doc);
Update update = Update.fromDocument(doc);

mongoTemplate.upsert(query, update, "descriptions");

это работает!