web-dev-qa-db-fra.com

MongoTemplate upsert - moyen facile de faire une mise à jour à partir de pojo (quel utilisateur a édité)?

Voici un simple pojo:

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

Et consultez le code suivant pour appliquer un upsert à MongoDb via 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");

La ligne pour générer l'objet Update spécifie manuellement chaque champ de la classe Item.

Mais si mon objet Item change, ma couche Dao se casse.

Existe-t-il donc un moyen d'éviter cela, afin que tous les champs de ma classe Item soient appliqués automatiquement à la mise à jour?

Par exemple.

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

Notez que mon pojo ne s'étend pas DBObject.

17
vikingsteve

J'ai rencontré le même problème. Dans la version actuelle de Spring Data MongoDB, rien de tel n'est disponible. Vous devez mettre à jour les champs séparés à la main.

Cependant c'est possible avec un autre framework: Morphia.

Ce framework a un wrapper pour la fonctionnalité DAO: https://github.com/mongodb/morphia/wiki/DAOSupport

Vous pouvez utiliser l'API DAO pour faire des choses comme ceci:

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

J'ai trouvé une assez bonne solution à cette question

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

Veuillez noter que Update.fromDBObject renvoie un objet de mise à jour avec tous les champs dans dbDoc. Si vous souhaitez simplement mettre à jour les champs non nuls, vous devez coder une nouvelle méthode pour exclure les champs nuls.

Par exemple, le front-end publie un document comme ci-dessous:

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

Il suffit de mettre à jour le champ 'langue':

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

La solution pour une nouvelle version Spring-data-mongodb 2.X.X.

L'API a évolué, depuis la version 2.X.X il y a:

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

au lieu de (1.X.X):

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

La solution complète:

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

Ça marche!

9
Dawid Naczke

vous pouvez utiliser save: (si non exist = insert else = upsert)

save (Object objectToSave, String collectionName)

lire: javadoc

6
Nimrod007

C'est ce que je fais pour le moment. Pas tellement une manière élégante de le faire, mais cela enregistre un précieux appel 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);
    }
}
2
Vivek Sethi

Si vous souhaitez ajouter Pojos incl. propriété String id; vous devez exclure le champ _id dans la méthode fromDBObject Update.fromDBObject(dbDoc,"_id").

Sinon, vous obtenez l'exception:

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}

car le champ _id du premier est nul

{
    "_id" : null,
...

}

Le code complet basé sur la réponse @PaniniGelato serait

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

Ensuite, l'upsert fonctionne dans les cas d'insertion et mise à jour. Les corrections et réflexions sont les bienvenues;)

2
Chris

Il y a ici deux cas à distinguer:

  1. Mettez à jour un élément précédemment récupéré dans la base de données.
  2. Mettez à jour ou insérez (insérez) un élément que vous avez créé par code.

Dans le cas 1), vous pouvez simplement utiliser mongoTemplate.save (pojo, "collection") , car votre POJO aura déjà un ObjectID rempli dans son champ id.

Dans le cas 2) Vous devez expliquer à mongo ce que "existe déjà" signifie dans le cas de votre modèle de domaine: Par défaut, la méthode mongoTemplate.save () met à jour un élément existant, s'il y en a un avec le même ObjectId. Mais avec un POJO nouvellement instancié, vous n'avez pas cet identifiant. Par conséquent, la méthode mongoTemplate.upsert () a un paramètre de requête que vous pouvez créer comme ceci:

MyDomainClass pojo = new MyDomainClass(...);

Query query = Query.query(Criteria.where("email").is("[email protected]"));

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);
1
Robert

Tout comme les réponses précédentes l'ont dit, utilisez les fonctions mongoTemplate.getConverter (). Write () et Update.fromDocument (). Mais j'ai trouvé Update.fromDocument () n'ajoutera pas la clé "$ set" et ne fonctionnera pas directement, la solution est d'ajouter vous-même "$ set", comme ci-dessous (PS: j'utilise la version 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));
}
1
arthurdai

Utilisez simplement ReflectionDBObject - si vous faites Description l'étendre, vous devez simplement transférer les champs de votre objet vers Update de manière réfléchie, automagiquement. La note ci-dessus sur les champs nuls inclus dans la mise à jour reste vraie.

0
V. F.
@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);
}
0
字云龙
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);
    }

}

c'est un moyen très simple d'enregistrer une chaîne json dans une collection en utilisant mongodb et spring. Cette méthode peut être remplacée pour être utilisée comme JSONObject.

0
Awanish Kumar

Je pense que: Description ajouter une propriété

@Id
private String id;

puis obtenez un document par la condition de requête, définissez l'ID de la description par l'ID du document. Et enregistrer

0
stoneHah