MongoTemplate upsert: forma sencilla de realizar una actualización desde pojo (¿qué usuario ha editado)?

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

Pregunta

Aquí hay un pojo simple:

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

Y consulte el siguiente código para aplicar un upsert a MongoDb a través de 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 línea para generar el Update objeto especifica cada campo del Item clase manualmente.

Pero si mi Item El objeto cambia y luego mi capa de Dao se rompe.

Entonces, ¿hay alguna manera de evitar hacer esto, de modo que todos los campos de mi Item ¿Las clases se aplican automáticamente a la actualización?

P.ej.

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

Fíjate que mi pojo no se extiende DBObject.

¿Fue útil?

Solución

Tuve el mismo problema.En la versión actual de Spring Data MongoDB no hay tal cosa disponible.Tienes que actualizar los campos separados a mano.

Sin embargo, es posible con otro marco:Morfina.

Este marco tiene un contenedor para la funcionalidad DAO: https://github.com/mongodb/morphia/wiki/DAOSupport

Puedes usar la API DAO para hacer cosas como esta:

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

Otros consejos

Encontré una solución bastante buena para esta pregunta.

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

Tenga en cuenta que Update.fromDBObject devuelve un objeto de actualización con todos los campos en dbDoc.Si solo desea actualizar campos que no sean nulos, debe codificar un nuevo método para excluir campos nulos.

Por ejemplo, el front-end publica un documento como el siguiente:

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

Sólo necesitamos actualizar el campo 'idioma':

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

La solución para una nueva versión spring-data-mongodb 2.X.X.

La API ha evolucionado, desde la versión 2.X.X existe:

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

en lugar de (1.X.X):

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

La solución completa:

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

¡Funciona!

puedes usar guardar:(si no existe = insertar otra cosa = insertar)

guardar (Objeto objeto para guardar, nombre de la colección de cadenas)

leer : javadoc

Esto es lo que estoy haciendo por el momento.No es una forma tan elegante de hacerlo, pero sí ahorra una preciosa llamada a la base de datos:

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

Si desea insertar Pojos incl.propiedad String id; tienes que excluir el campo _id en el método fromDBObject Update.fromDBObject(dbDoc,"_id").

De lo contrario obtienes la excepción:

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}

porque el campo _id del primero es nulo

{
    "_id" : null,
...

}

El código completo basado en la respuesta de @PaniniGelato sería

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

Entonces el upsert está funcionando en los casos de inserción. y actualizar.Correcciones y pensamientos son bienvenidos;)

Aquí hay que distinguir dos casos:

  1. Actualice un elemento que se obtuvo previamente de la base de datos.
  2. Actualice o inserte (upsert) un elemento que creó mediante código.

En el caso 1) simplemente puede usar mongoTemplate.save(pojo, "colección"), porque su POJO ya tendrá un ObjectID completo en su campo de identificación.

En el caso 2) Tienes que explicarle a mongo qué significa "ya existe" en el caso de tu modelo de dominio:De forma predeterminada, el método mongoTemplate.save() actualiza un elemento existente, si hay uno con el mismo ObjectId.Pero con un POJO recién creado no tienes esa identificación.Por lo tanto, el método mongoTemplate.upsert() tiene un parámetro de consulta que puedes crear así:

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

Creo que:Descripción agregar una propiedad

@Id
private String id;

luego obtenga un documento según la condición de consulta, establezca la identificación de la descripción según la identificación del documento.y guarda

Solo usa ReflectionDBObject - Si tu haces Description ampliarlo, deberías transferir los campos de tu objeto a Update reflexivamente, automágicamente.La nota anterior sobre los campos nulos incluidos en la actualización sigue siendo válida.

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

}

Esta es una forma muy simple de guardar la cadena JSON en la colección usando MongoDB y Spring.Este método se puede anular para utilizarlo como 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);
}

Tal como dijeron las respuestas anteriores, use las funciones mongoTemplate.getConverter().write() y Update.fromDocument().Pero descubrí que Update.fromDocument() no agrega la clave "$set" y no funciona directamente, la solución es agregar "$set" usted mismo, como se muestra a continuación (PD:Estoy usando la versión 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));
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top