spring-data-mongo使用MappingMongoConverter更新文档中的空值

时间:2015-05-06 08:33:23

标签: spring-data spring-data-mongodb

使用spring data mongo,我需要在mongo中更新文档。

我的实体定义如下:

@Document(collection = "Orders")
public class Order{

    @Id
    private Long id;
    private String clientContainerReference;
    private String status;
    private Bigdecimal amount;
    private BigDecimal remainingQuantity;
    ...
}

首先,将此文档插入mongo中,其余值为100。 接下来,使用null remainingQuantity更新订单。 更新(upsert)后,remainingQuantity始终设置为100.

这是由于班级:

org.springframework.data.mongodb.core.convert.MappingMongoConverter

在writeInternal方法中,对文档的每个属性都进行了空检查。如果属性为null,则从生成的DBObject中删除它。

entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
        public void doWithPersistentProperty(MongoPersistentProperty prop) {

            if (prop.equals(idProperty)) {
                return;
            }

            Object propertyObj = wrapper.getProperty(prop);

            if (null != propertyObj) {

                if (!conversions.isSimpleType(propertyObj.getClass())) {
                    writePropertyInternal(propertyObj, dbo, prop);
                } else {
                    writeSimpleInternal(propertyObj, dbo, prop);
                }
            }
        }
    });

我可以理解它更高效,因为生成DBObject更小,更新请求更适合mongo。 但是我怎样才能更新真正的空值?

更具体地说,在我的例子中,所有文档的所有字段都可以为null。所以我不想编写自定义转换器并将每个java字段逐个映射到DBObject Field。

对于我的更多用例,我创建了一个“NullAwareMappingMongoConverter”,它覆盖MappingMongoConverter,让转换器写入值,如果它是一个空值。

entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
        public void doWithPersistentProperty(MongoPersistentProperty prop) {

            if (prop.equals(idProperty)) {
                return;
            }


            Object propertyObj = wrapper.getProperty(prop);

            if (null != propertyObj) {

                if (!conversions.isSimpleType(propertyObj.getClass())) {
                    writePropertyInternal(propertyObj, dbo, prop);
                } else {
                    writeSimpleInternal(propertyObj, dbo, prop);
                }
            }
            else{
                writeSimpleInternal(propertyObj, dbo, prop);
            }
        }
    });

这是一个非常流畅的解决方案,因为,MappingMongoConverter对于spring数据mongo具有包可见性。 spring是否提供了一种方法:不要使用注释或其他东西忽略此属性的空值?

谢谢

这是用于更新实体的代码

 public T setNotificationDateAndSave(T entity) {
    Assert.notNull(entity, "Entity must not be null!");
    BasicDBObject dbObject = new BasicDBObject();
    mongoTemplate.getConverter().write(entity, dbObject);

    DateTime expirationDate = getDeprecatedStatus().contains(getStatus(entity)) ?
            new DateTime().plusSeconds(EXPIRE_AFTER_SECONDS) : null;

    dbObject.put(EXPIRATION_DATE_COLUMN, mongoTemplate.getConverter()
            .convertToMongoType(expirationDate, ClassTypeInformation.from(DateTime.class)));


    NotificationDateUpdate update = new NotificationDateUpdate(dbObject, NOTIFICATION_DATE_COLUMN);
    Query q = Query.query(Criteria.where("_id").is(entity.getId()).andOperator(Criteria.where(REGISTER_DATE_COLUMN).lte(entity.getRegisterDate())));
    try{
        mongoTemplate.upsert(q, update, parameterClass);
    }catch (DuplicateKeyException e) {
        logger.info(format("could not save notification : a more recent notification exists for collection %s and entity id : %s", parameterClass.getName(), entity.getId()));
    }
    return entity;
}

1 个答案:

答案 0 :(得分:0)

同样的问题,但是我不想弄乱转换器,感觉太复杂了。 我只是为对象自动取消了所有空字段的设置。

稍微适应您的代码,看起来像:

DBObject dbObject = new BasicDBObject();
mongoTemplate.getConverter().write(entity, dbObject);
Update update = Update.fromDBObject(dbObject);
for (Field f : entity.class.getDeclaredFields()) {
  try {
     f.setAccessible(true);
     if (f.get(entity) == null)
        update.unset(f.getName());
  } catch (IllegalAccessException e) {
     e.printStackTrace();
  }
}
ops.upsert(query,update);