如何在Spring Data MongoDB中拦截MongoRepository并追加查询条件?
Spring Data MongoDB 统一追加查询Criteria的实现方案
下面提供几种可行的实现方式,按需选择:
方式一:自定义Repository基类重写查询逻辑
通过继承SimpleMongoRepository自定义Repository基类,在查询执行前统一追加Criteria条件。
- 编写自定义基类:
public class CustomMongoRepositoryImpl<T, ID> extends SimpleMongoRepository<T, ID> { private final MongoEntityInformation<T, ID> entityInformation; private final MongoOperations mongoOperations; public CustomMongoRepositoryImpl(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations) { super(entityInformation, mongoOperations); this.entityInformation = entityInformation; this.mongoOperations = mongoOperations; } // 重写Example查询方法,追加统一条件 @Override public List<T> findAll(Example<T> example) { Query query = Query.byExample(example); appendUnifiedCriteria(query); return mongoOperations.find(query, entityInformation.getJavaType()); } // 重写派生查询的核心方法,覆盖所有自动生成的查询 @Override protected Query createQuery(Example<T> example) { Query query = super.createQuery(example); appendUnifiedCriteria(query); return query; } // 统一追加条件的逻辑(示例:从上下文获取国家信息) private void appendUnifiedCriteria(Query query) { String currentCountry = getCurrentCountryFromContext(); if (currentCountry != null) { query.addCriteria(Criteria.where("country").is(currentCountry)); } } // 从上下文获取业务参数的示例方法 private String getCurrentCountryFromContext() { return RequestContextHolder.getRequestAttributes() != null ? ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("X-Country") : null; } }
- 配置Repository使用自定义基类:
在Spring配置类中指定自定义基类:
@Configuration @EnableMongoRepositories( basePackages = "com.yourpackage.repository", repositoryBaseClass = CustomMongoRepositoryImpl.class ) public class MongoConfig { }
方式二:实现QueryPostProcessor拦截查询
Spring Data MongoDB提供QueryPostProcessor接口,可在查询执行前直接修改Query对象,无需重写Repository基类。
- 实现QueryPostProcessor:
@Component public class NameRepositoryQueryPostProcessor implements QueryPostProcessor<MongoQuery, Query> { @Override public Query postProcess(MongoQuery mongoQuery, Query query) { // 仅拦截NameRepository的查询 if (NameRepository.class.isAssignableFrom(mongoQuery.getRepositoryInterface())) { String currentCountry = getCurrentCountryFromContext(); if (currentCountry != null) { query.addCriteria(Criteria.where("country").is(currentCountry)); } } return query; } private String getCurrentCountryFromContext() { return RequestContextHolder.getRequestAttributes() != null ? ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("X-Country") : null; } }
该方式会自动被Spring Data MongoDB识别,拦截所有Mongo查询,可通过mongoQuery.getRepositoryInterface()精准控制拦截范围。
方式三:AOP切面拦截Repository查询方法
通过AOP直接拦截NameRepository的查询方法,手动修改或重建查询条件。
- 编写AOP切面:
@Aspect @Component public class NameRepositoryQueryAspect { @Autowired private MongoTemplate mongoTemplate; // 拦截NameRepository中所有以find开头的查询方法 @Around("execution(* com.yourpackage.repository.NameRepository.find*(..))") public Object interceptQuery(ProceedingJoinPoint joinPoint) throws Throwable { Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); Object[] args = joinPoint.getArgs(); // 构建Spring Data自动生成的查询对象 MongoEntityInformation<Name, String> entityInfo = MongoEntityInformationSupport.getEntityInformation(Name.class, mongoTemplate); QueryLookupStrategy lookupStrategy = new MongoQueryLookupStrategy(mongoTemplate, mongoTemplate.getConverter(), null); RepositoryQuery repositoryQuery = lookupStrategy.resolveQuery(method, NameRepository.class, mongoTemplate.getConverter()); if (repositoryQuery instanceof AbstractMongoQuery) { Query query = ((AbstractMongoQuery) repositoryQuery).createQuery(args); // 追加统一条件 String currentCountry = getCurrentCountryFromContext(); if (currentCountry != null) { query.addCriteria(Criteria.where("country").is(currentCountry)); } // 根据方法返回类型执行查询 Class<?> returnType = method.getReturnType(); if (List.class.isAssignableFrom(returnType)) { return mongoTemplate.find(query, Name.class); } else if (Name.class.isAssignableFrom(returnType)) { return mongoTemplate.findOne(query, Name.class); } } // 无法处理时执行原方法 return joinPoint.proceed(); } private String getCurrentCountryFromContext() { return RequestContextHolder.getRequestAttributes() != null ? ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("X-Country") : null; } }
注意事项
getCurrentCountryFromContext()仅为示例,实际可根据业务需求从ThreadLocal、用户会话、配置中心等渠道获取条件参数。- 若需要按需追加条件,可在逻辑中增加判断(如某些特定查询不需要追加时跳过)。
- 方式二的
QueryPostProcessor要求Spring Data MongoDB版本在2.2及以上。
内容的提问来源于stack exchange,提问作者victorli
相关产品推荐
相关产品推荐

