搭一个新项目,从mongo数据库中查询数据,我直接使用的spring-data-mongodb模块。直接创建Repository接口,继承MongoRepository
public interface ResourceRepository extends MongoRepository {
}
使用MongoRepository的findById方法,查询"_id"为某个值,程序中查询不出来,但是数据库中有值。并且使用db.getCollection(‘resource’).find({"_id":})验证过。
然后查询的时候debug了下,走到了MongoTemplate的findById方法中,它将_id替换为了id:
public T findById(Object id, Class entityClass, String collectionName) {Assert.notNull(id, "Id must not be null!");Assert.notNull(entityClass, "EntityClass must not be null!");Assert.notNull(collectionName, "CollectionName must not be null!");MongoPersistentEntity> persistentEntity = mappingContext.getPersistentEntity(entityClass);String idKey = ID_FIELD;//_idif (persistentEntity != null) {if (persistentEntity.getIdProperty() != null) {idKey = persistentEntity.getIdProperty().getName();//赋值为id}}return doFindOne(collectionName, new Document(idKey, id), new Document(), entityClass);}
idKey = persistentEntity.getIdProperty().getName();这句代码把“_id”的key换为了“id”,这样如果mongo集合中只有“_id”字段,就查询不出来了。
解决方案
继承MongoTemplate类,重写它的findById方法,我Debug后MongoTemplate执行的是public MongoTemplate(MongoDbFactory mongoDbFactory, @Nullable MongoConverter mongoConverter) 的构造方法:![在这里插入图片描述](https://img.php1.cn/3cd4a/9b0d/ae9/2d998ad7838fbf16.jpeg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0NTM1MTU=,size_16,color_FFFFFF,t_70)
我们也创建类似的构造方法:
@Component
public class PMongoTemplate extends MongoTemplate{private static final String ID_FIELD = "_id";@Autowiredpublic PMongoTemplate(MongoDbFactory mongoDbFactory, @Nullable MongoConverter mongoConverter) {super(mongoDbFactory,mongoConverter);}@Overridepublic T findById(Object id, Class entityClass, String collectionName) {Assert.notNull(id, "Id must not be null!");Assert.notNull(entityClass, "EntityClass must not be null!");Assert.notNull(collectionName, "CollectionName must not be null!");String idKey = ID_FIELD;return doFindOne(collectionName, new Document(idKey, id), new Document(), entityClass);}
}
启动之后报如下错误,没有创建Bean ‘mongoTemplate’:
因为我们自定了一个MongoTemplate类型的Bean,所以Spring在初始化的时候,不不再创建同一类型的了。所以我们之前创建的ResourceRepository等接口都无法使用了,这些接口继承MongoRepository,启动后创建对应的SimpleMongoRepository,SimpleMongoRepository间接依赖了mongoTemplate。
这样走不通,那我们就用MongoTemplate自带的方法:
protected T doFindOne(String collectionName, Document query, Document fields, Class entityClass) {MongoPersistentEntity> entity = mappingContext.getPersistentEntity(entityClass);Document mappedQuery = queryMapper.getMappedObject(query, entity);Document mappedFields = queryMapper.getMappedObject(fields, entity);if (LOGGER.isDebugEnabled()) {LOGGER.debug("findOne using query: {} fields: {} for class: {} in collection: {}", serializeToJsonSafely(query),mappedFields, entityClass, collectionName);}return executeFindOneInternal(new FindOneCallback(mappedQuery, mappedFields),new ReadDocumentCallback(this.mongoConverter, entityClass, collectionName), collectionName);}@Nullable@Overridepublic T findOne(Query query, Class entityClass, String collectionName) {Assert.notNull(query, "Query must not be null!");Assert.notNull(entityClass, "EntityClass must not be null!");Assert.notNull(collectionName, "CollectionName must not be null!");if (ObjectUtils.isEmpty(query.getSortObject()) && !query.getCollation().isPresent()) {return doFindOne(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass);} else {query.limit(1);List results = find(query, entityClass, collectionName);return results.isEmpty() ? null : results.get(0);}}
doFindOne是protected修饰的我们无法直接调用,findOne可以调用,我们只需要创建Query对象,将条件传递进去即可,下面是简单的实现:
@Component
public class MongoQLService {@AutowiredMongoTemplate mongoTemplate;public T findById(Object id,Class entityClass,String collectionName) {Query query = new Query();query.addCriteria( new Criteria("_id").is(id));return mongoTemplate.findOne(query, entityClass, collectionName);}
}
关于Query的构建,参考Spring Data MongoDB 基本文档查询