关于Spring中MongoTemplate.aggregate的一个奇异bug
来源:互联网 发布:上海it helpdesk招聘 编辑:程序博客网 时间:2024/04/30 11:35
昨天在完成公司一个项目的时候用了mongoTemplate的aggregate,在使用Aggretaion.match(criteria)来筛选数据,其中criteria语句是Criteria.where("id").ne(xxxId),结果程序在执行的时候该条件一直没有起效果,但是其他的find和update等语句都是可以执行的,抱着满脑袋的疑惑翻看了它的源码实现后发现这里有一个很大的坑,具体是这个样子的:
在其他语句中,criteria查询条件最终都会包到Query对象里面,而template驱动在对Query进行解析的时候,以updateFirst为例,它的解析过程是这个样子的:
public WriteResult updateFirst(Query query, Update update, Class<?> entityClass, String collectionName) { return doUpdate(collectionName, query, update, entityClass, false, false);}
首先经过updateFirst方法,然后调用doUpdate:
protected WriteResult doUpdate(final String collectionName, final Query query, final Update update, final Class<?> entityClass, final boolean upsert, final boolean multi) { return execute(collectionName, new CollectionCallback<WriteResult>() { public WriteResult doInCollection(DBCollection collection) throws MongoException, DataAccessException { MongoPersistentEntity<?> entity = entityClass == null ? null : getPersistentEntity(entityClass); increaseVersionForUpdateIfNecessary(entity, update); DBObject queryObj = query == null ? new BasicDBObject() : queryMapper.getMappedObject(query.getQueryObject(), entity); DBObject updateObj = update == null ? new BasicDBObject() : updateMapper.getMappedObject( update.getUpdateObject(), entity); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Calling update using query: " + queryObj + " and update: " + updateObj + " in collection: " + collectionName); } MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName, entityClass, updateObj, queryObj); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); WriteResult writeResult = writeConcernToUse == null ? collection.update(queryObj, updateObj, upsert, multi) : collection.update(queryObj, updateObj, upsert, multi, writeConcernToUse); if (entity != null && entity.hasVersionProperty() && !multi) { if (writeResult.getN() == 0 && dbObjectContainsVersionProperty(queryObj, entity)) { throw new OptimisticLockingFailureException("Optimistic lock exception on saving entity: " + updateObj.toMap().toString() + " to collection " + collectionName); } } handleAnyWriteResultErrors(writeResult, queryObj, MongoActionOperation.UPDATE); return writeResult; } });}
在这一步中,它对query的解析是这句:
DBObject queryObj = query == null ? new BasicDBObject() : queryMapper.getMappedObject(query.getQueryObject(),
我们跟入getMappedObject方法中,它的实现是:
public DBObject getMappedObject(DBObject query, MongoPersistentEntity<?> entity) { if (isNestedKeyword(query)) { return getMappedKeyword(new Keyword(query), entity); } DBObject result = new BasicDBObject(); for (String key : query.keySet()) { // TODO: remove one once QueryMapper can work with Query instances directly if (Query.isRestrictedTypeKey(key)) { @SuppressWarnings("unchecked") Set<Class<?>> restrictedTypes = (Set<Class<?>>) query.get(key); this.converter.getTypeMapper().writeTypeRestrictions(result, restrictedTypes); continue; } if (isKeyword(key)) { result.putAll(getMappedKeyword(new Keyword(query, key), entity)); continue; } Field field = createPropertyField(entity, key, mappingContext); Entry<String, Object> entry = getMappedObjectForField(field, query.get(key)); result.put(entry.getKey(), entry.getValue()); } return result;}
在一个个地翻看每一条语句的源码之后,发现其对key进行解析的时候,getMappedObjectForfield方法负责对每一个约束key和查询值进行一一对应转换,具体实现是:
protected Entry<String, Object> getMappedObjectForField(Field field, Object rawValue) { String key = field.getMappedKey(); Object value; if (isNestedKeyword(rawValue) && !field.isIdField()) { Keyword keyword = new Keyword((DBObject) rawValue); value = getMappedKeyword(field, keyword); } else { value = getMappedValue(field, rawValue); } return createMapEntry(key, value);}
到了这儿的时候,其中一个条件引起了我的注意,就是field.isIdField,难道是这里对id进行了转换?
进一步跟进之后发现:
public boolean isIdField() { return ID_KEY.equals(name);}
private static final String ID_KEY = "_id";
原来其在此处对_id进行了特殊处理,既然有做特殊处理,那就得看看是怎么特殊处理的了,于是进一步跟进getMappedKeyword方法,发现其中没有进一步特殊处理,于是返回上一个方法,从第一个方法跟进,打开getMappedKey:
public String getMappedKey() { return isIdField() ? ID_KEY : name;}
原来在这里进行了特殊处理,随后进行了进一步查看之后发现template在Aggregation.match中没有对此进行转换,结果导致mongo查询条件一直没有正常工作,没办法,只能自行处理了,于是查询条件改为Criteria.where("_id").ne(new ObjectId(xxxId)),至此搞定。这个坑埋得有点深啊。
- 关于Spring中MongoTemplate.aggregate的一个奇异bug
- 关于Spring中MongoTemplate排序问题
- mongodb和spring集成中MongoTemplate的总结是使用方法
- mongodb和spring集成中MongoTemplate的总结是使用方法
- Spring 的单个MongoTemplate配置中加入用户认证信息
- php中关于session的一个bug
- dedecms中一个关于session的bug
- 关于培训中碰到的一个Bug
- spring 事务中遇到的----奇异问题
- Spring结合mongotemplate遇到的问题
- Spring.net 的一个bug ?
- 关于javascript中parseInt函数的一个所谓的bug
- 关于javascript中parseInt函数的一个所谓的bug
- 关于Qt solution中qtwinmigrate framework的一个bug
- extjs 中store 关于baseParams的一个bug
- 关于vs2010中Chart控件的一个小BUG
- magento salerule中关于attribute的一个bug
- WinSock中关于阻塞接收/发送超时的一个BUG
- 直接插入排序
- Xcode调试之View Memory(查看内存)
- 白盒测试
- OSError: [Errno 1] Operation not permitted
- python爬虫基本数据类型
- 关于Spring中MongoTemplate.aggregate的一个奇异bug
- HDU1241(DFS求连通块)
- C开发经验3:如何避免重复包含头文件多次
- mybatis (十二) 逆向工程
- Sql_Server编程 简明教程
- Android的四层结构
- ,有一款RESTFUL接口的文档在线自动生成+功能测试功能软件——Swagger UI,具体配置过程可移步《Spring Boot 利用 Swagger 实现restful测试》
- JSON.NET VS BinaryFormatter 性能
- 蓝桥杯 2012 3 拼音字母