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

StackOverflow https://stackoverflow.com//questions/20001627

Вопрос

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

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

И, пожалуйста, посмотрите следующий код, чтобы применить upsert в MongoDb через Spring 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 объект меняется, тогда мой слой Дао ломается.

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

Например.

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

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

Это было полезно?

Решение

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

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

Этот фреймворк имеет оболочку для функциональности DAO: https://github.com/mongodb/morphia/wiki/DAOSupport

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

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");

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

Например, внешний интерфейс опубликует документ, как показано ниже:

//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);

Решение для новой версии Spring-data-mongodb 2.X.X.

API претерпел изменения, начиная с версии 2.X.X:

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");

Оно работает!

вы можете использовать сохранение:(если не существует = вставить еще = обновить)

save (Object objectToSave, имя коллекции строк)

читать : Javadoc

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

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);
    }
}

Если хотите прошить 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,
...

}

Полный код, основанный на ответе @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 работает в случаях вставки и обновлять.Поправки и мысли приветствуются ;)

Здесь следует различать два случая:

  1. Обновите элемент, который ранее был получен из БД.
  2. Обновите или вставьте (upsert) элемент, созданный вами с помощью кода.

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

В случае 2) вы должны объяснить Монго, что означает «уже существует» в случае вашей модели домена:По умолчанию метод 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 и Spring.Этот метод можно переопределить для использования в качестве JSONObject.

@Override
public void updateInfo(UpdateObject algorithm) {
    Document document = new Document();
    mongoTemplate.getConverter().write(algorithm, document);
    Update update = Update.fromDocument(document);
    mongoTemplate.updateFirst(query(where("_id").is(algorithm.get_id())), update, UpdateObject.class);
}

Как говорилось в предыдущих ответах, используйте функции mongoTemplate.getConverter().write() и Update.fromDocument().Но я обнаружил, что Update.fromDocument() не добавляет ключ «$set» и не работает напрямую. Решение состоит в том, чтобы добавить «$set» самостоятельно, как показано ниже (PS:Я использую версию 2.2.1.RELEASE):

public static Update updateFromObject(Object object, MongoTemplate mongoTemplate) {
    Document doc = new Document();
    mongoTemplate.getConverter().write(object, doc);
    return Update.fromDocument(new Document("$set", doc));
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top