MongoRepository的findById方法查不出_id值的解決方案.md

搭一個新項目,從mongo數據庫中查詢數據,我直接使用的spring-data-mongodb模塊。直接創建Repository接口,繼承MongoRepository

public interface ResourceRepository extends MongoRepository<Resource, Long> {
}

使用MongoRepository的findById方法,查詢"_id"爲某個值,程序中查詢不出來,但是數據庫中有值。並且使用db.getCollection(‘resource’).find({"_id":})驗證過。
然後查詢的時候debug了下,走到了MongoTemplate的findById方法中,它將_id替換爲了id:

public <T> T findById(Object id, Class<T> 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;//_id

        if (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) 的構造方法:在這裏插入圖片描述
我們也創建類似的構造方法:

@Component
public class PMongoTemplate extends MongoTemplate{
    private static final String ID_FIELD = "_id";
    
    @Autowired
    public PMongoTemplate(MongoDbFactory mongoDbFactory, @Nullable MongoConverter mongoConverter) {
        super(mongoDbFactory,mongoConverter);
    }
    
    @Override
    public <T> T findById(Object id, Class<T> 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> T doFindOne(String collectionName, Document query, Document fields, Class<T> 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<T>(this.mongoConverter, entityClass, collectionName), collectionName);
    }

@Nullable
    @Override
    public <T> T findOne(Query query, Class<T> 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<T> results = find(query, entityClass, collectionName);
            return results.isEmpty() ? null : results.get(0);
        }
    }

doFindOne是protected修飾的我們無法直接調用,findOne可以調用,我們只需要創建Query對象,將條件傳遞進去即可,下面是簡單的實現:

@Component
public class MongoQLService {

    @Autowired
    MongoTemplate mongoTemplate;
    
    
    public <T>  T findById(Object id,Class<T> entityClass,String collectionName) {
        Query query  = new Query();
        query.addCriteria( new Criteria("_id").is(id));
        return  mongoTemplate.findOne(query, entityClass, collectionName);
    }
}

關於Query的構建,參考Spring Data MongoDB 基本文檔查詢

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章