With spring data mongo, i need to update document in mongo.
使用spring data mongo,我需要在mongo中更新文档。
My entity is define like this :
我的实体定义如下:
@Document(collection = "Orders")
public class Order{
@Id
private Long id;
private String clientContainerReference;
private String status;
private Bigdecimal amount;
private BigDecimal remainingQuantity;
...
}
At first, this document is inserted in mongo with a remainingQuantity of 100. Next, the order is updated with a null remainingQuantity. After the update (upsert), the remainingQuantity is always set to 100.
首先,将此文档插入mongo中,其余值为100.接下来,使用null remainingQuantity更新订单。更新(upsert)后,remainingQuantity始终设置为100。
This is due to the class :
这是由于班级:
org.springframework.data.mongodb.core.convert.MappingMongoConverter
in the method writeInternal a null check is done on every property of the document. If the property is null, it is exclued from the generated DBObject.
在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);
}
}
}
});
I can understand that it is more efficient because the generate DBObject is smaller and the update request is more digest for mongo. But how can i update real null values ?
我可以理解它更有效,因为生成DBObject更小,更新请求更适合mongo。但是我怎样才能更新真正的空值?
More specific, in my case, all fields of all document could be null. so i doesn't want to write custom converter and map one by one each java field to DBObject Field.
更具体地说,在我的例子中,所有文档的所有字段都可以为null。所以我不想编写自定义转换器并将每个java字段逐个映射到DBObject Field。
For more my usecase, i have created a "NullAwareMappingMongoConverter" that override MappingMongoConverter to let the converter write the value if it is a null value.
对于我的更多用例,我创建了一个“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);
}
}
});
It's an very uggly solution, because, the MappingMongoConverter For spring data mongo has a package visibility. Does spring provide a way to tell : don't ignore null value on this properties with annotation or something else ?
这是一个非常流畅的解决方案,因为,MappingMongoConverter对于spring数据mongo具有包可见性。 spring是否提供了一种告诉方法:不要在带有注释或其他内容的此属性上忽略空值?
Thank you
here is the code used to update the entity
这是用于更新实体的代码
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 个解决方案
#1
Same problem, but I didn't want to mess with the converter, it felt too complicated for the need. I simply automated the unsetting of all null fields for my object.
同样的问题,但我不想弄乱转换器,它感觉太复杂了需要。我只是为我的对象自动取消了所有空字段的设置。
Slightly adapted to your code, it would look like:
略微适应您的代码,它看起来像:
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);
#1
Same problem, but I didn't want to mess with the converter, it felt too complicated for the need. I simply automated the unsetting of all null fields for my object.
同样的问题,但我不想弄乱转换器,它感觉太复杂了需要。我只是为我的对象自动取消了所有空字段的设置。
Slightly adapted to your code, it would look like:
略微适应您的代码,它看起来像:
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);