查询方法定义的两种方式
- 根据方法名来自动推测
- 自定义
本文介绍自定义的方式
@Query注解
Mongodb使用的是基于json的查询语句。
通过将org.springframework.data.mongodb.repository.Query批注添加到存储库查询方法,可以指定要使用的MongoDB JSON查询字符串,而不是从方法名称派生查询,如以下示例所示:
public interface PersonRepository extends MongoRepository<Person, String>
@Query("{ 'firstname' : ?0 }")
List<Person> findByThePersonsFirstname(String firstname);
}
占位符 ?0 是函数的参数。
注意: String类型的参数在绑定过程中会进行转义, 这意味着不能为之添加特殊的参数。
可以使用fields来设置返回的字段:
@Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname' : 1, 'lastname' : 1}")
List<Person> findByThePersonsFirstname(String firstname);
上例中结果Person对象中只会有firstname、lastname 和id 属性 , 其他属性没有 。
在基于json的查询中使用SpEL表达式
查询串和field返回定义可以使用SpEL表达式 在运行时进行动态创建 。
表达式通过包含所有参数的数组公开方法参数。 以下查询使用[0]声明lastname的谓词值(相当于?0参数绑定):
public interface PersonRepository extends MongoRepository<Person, String>
@Query("{'lastname': ?#{[0]} }")
List<Person> findByQueryWithExpression(String param0);
}
当传入参数为对象时, 实例:
@Query(value="{'name': ?#{ [0].name }}")
public Page<RcControllJournalDo> querylikepages(RcControllJournalDo mdo, Pageable pageable);
上例等价于 where name = mdo.name .
更复杂的实例:
/**
* 当mdo.name为空时, 查询条件为 { "name" : { "$exists" : true } } ,即查询所有name列存在的记录(包括值为null的记录,但是对于没有name列的查询不到) ;
* 当mdo.name不空时,查询条件为 { "name" : [0].name }
*/
@Query(value=" { 'name': ?#{ ([0].name == null) or ([0].name.length() == 0) ? '{$exists:true}' : [0].name } } ")
public Page<RcControllJournalDo> querylikepages2(RcControllJournalDo mdo, Pageable pageable);
#{ ([0].name == null) or ([0].name.length() == 0) ? '{$exists:true}' : [0].name }
为SpEL表达式 (三目表达式)。
模糊查询例子:
/**
* 使用正则表达式模糊查询
*/
@Query(value=" { 'idno': ?#{ ([0].name == null) or ([0].name.length() == 0) ? {$exists:true} : {$regex: [0].name } } } ")
public Page<RcControllJournalDo> querylikepages21(RcControllJournalDo mdo, Pageable pageable);
mongodb的正则表达式查询语法为:
>db.posts.find({post_text:{$regex:"runoob"}})
>db.posts.find({post_text:{$regex:"runoob",$options:"$i"}})
例子:
根据前端上送的查询条件模糊匹配name 和idno , 当有值时查询之,无则查询所有:
/**
* 模糊查询name 和 idno <br>
* 1. mongodb or语法 :{ $or :[{}, {},...] } 例子: {$or:[{"by":"aaa"} , {"title": "bbb"}]} , 即 where by=aaa or title=bbb <BR>
* 2. { $or :[{'name' : ?#{}}, {'idno' : ?#{}}] } <br>
*
*/
@Query(value=" { $or :[{'name' : ?#{ ([0].name == null) or ([0].name.length() == 0) ? '{$exists:true}' : {$regex:[0].name} }},"
+ " {'idno' : ?#{ ([0].idno == null) or ([0].idno.length() == 0) ? '{$exists:true}' : {$regex: [0].idno} }}] } ")
public Page<RcControllJournalDo> querylikepages3(RcControllJournalDo mdo, Pageable pageable);
输入参数:
mdo.setName("宋");
mdo.setIdno("112");
打印的日志为:
find using query: { "$or" : [{ "name" : { "$regex" : "宋" } }, { "idno" : { "$regex" : "112" } }] }
自定义Query方法
自定义repo的方法见其他博客部分, 大致步骤如下:
- 创建接口
- 创建实现类 (命名与接口名一致,且以Impl结尾)
- 在实现类中实现方法, 可以使用MongoTemplate 或 其他数据源的模板
- 让repo接口继承该自定义接口
- 直接使用repo调用即可。
下面着重介绍使用Query 、Criteria 来创建查询条件 并使用分页:
public Page<RcControllJournalDo> selectSearchNameIdno(RcControllJournalDo mdo, Pageable pageable) {
Query query = new Query();
logger.debug("开始搜风控流水,使用姓名和身份证号模糊匹配:" + mdo.getName() );
if (StringUtils.isNotBlank(mdo.getName()) && StringUtils.isNotBlank(mdo.getIdno())) {
query.addCriteria(new Criteria().orOperator(Criteria.where("name").regex(mdo.getName()),
Criteria.where("idno").regex(mdo.getIdno())));
}
// 分页 和 排序
query.with(pageable);
query.with(new Sort(Direction.DESC, "dateTm"));
long totoal = this.mongoTemplate.count(query, RcControllJournalDo.class);
logger.debug("查询统计总条数 :" + totoal);
logger.debug("分页参数:" + pageable.getPageNumber() + ";" + pageable.getPageSize());
List<RcControllJournalDo> res = this.mongoTemplate.find(query , RcControllJournalDo.class);
logger.debug("查询结束:" + res.size());
return new PageImpl<RcControllJournalDo>(res, pageable, totoal);
}
缺点是, 总条数需要主动查询 。
query内部会根据上送的分页条件,综合使用skip 、limit 来实现分页效果。