1. 概述
本文将重点介绍如何在Spring Data MongoDB中构建各种类型的查询语句,使用不同的方法:
- 使用Query和Criteria类
- 自动生成的查询方法
- 利用@Query使用JSON查询
- 使用QueryDSL
关于Maven依赖配置,请翻阅我们上一篇文章。
2. 文档查询
在Spring Data使用Query
和Criteria
类来查询 MongoDB是最常用的方式之一,它非常接近MongoDB的原生操作符。
2.1. is - 等于查询
is
是最简单的等于
查询操作。
下面例子中,我们查询名为Eric
的用户。
数据库中的数据::
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.baeldung.model.User",
"name" : "Antony",
"age" : 55
}
}
代码实现
Query query = new Query();
query.addCriteria(Criteria.where("name").is("Eric"));
List<User> users = mongoTemplate.find(query, User.class);
返回结果:
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
}
2.2. regex 正则表达式
正则表达式是更灵活强大的查询类型。使用MongoDB $regex
,返回所有匹配的记录。
下面我们查询名字以A
开头的用户。
数据库中原始数据:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.baeldung.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.baeldung.model.User",
"name" : "Alice",
"age" : 35
}
]
查询代码:
Query query = new Query();
query.addCriteria(Criteria.where("name").regex("^A"));
List<User> users = mongoTemplate.find(query,User.class);
查询结果,返回2条匹配的记录::
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.baeldung.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.baeldung.model.User",
"name" : "Alice",
"age" : 35
}
]
另一个例子,返回名字以c
结尾的用户:
Query query = new Query();
query.addCriteria(Criteria.where("name").regex("c$"));
List<User> users = mongoTemplate.find(query, User.class);
结果:
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
}
2.3. lt、gt - 大于、小于查询
lt
和gt
方法对应mongo的$lt
(小于)和$gt
(大于)操作符。
下面例子中,我们查询年龄介于20-50之间的所有用户。
数据库初始数据:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.baeldung.model.User",
"name" : "Antony",
"age" : 55
}
}
查询代码:
Query query = new Query();
query.addCriteria(Criteria.where("age").lt(50).gt(20));
List<User> users = mongoTemplate.find(query,User.class);
查询结果:
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
}
2.4. 排序
Sort
用来指定排序方式。
下面例子中,将用户按年龄升序排序后返回。
初始数据:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.baeldung.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.baeldung.model.User",
"name" : "Alice",
"age" : 35
}
]
查询代码:
Query query = new Query();
query.with(Sort.by(Sort.Direction.ASC, "age"));
List<User> users = mongoTemplate.find(query,User.class);
返回的结果按照年龄排序:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.baeldung.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.baeldung.model.User",
"name" : "Alice",
"age" : 35
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
}
]
2.5. 分页查询
数据库中的数据:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.baeldung.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.baeldung.model.User",
"name" : "Alice",
"age" : 35
}
]
查询逻辑中,我们只请求pagesize为2的一页数据:
final Pageable pageableRequest = PageRequest.of(0, 2);
Query query = new Query();
query.with(pageableRequest);
结果和预期一样,只返回2条记录:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581907"),
"_class" : "org.baeldung.model.User",
"name" : "Eric",
"age" : 45
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.baeldung.model.User",
"name" : "Antony",
"age" : 33
}
]
3. 自动生成的查询方法
另一种常用方法是,Spring Data 支持根据方法名字自动生成查询语句。
我们只需要做的是,定义Repository
接口,并遵循Spring Data规范声明查询方法名字就行。
public interface UserRepository
extends MongoRepository<User, String>, QueryDslPredicateExecutor<User> {
...
}
3.1. FindByX
FindByX
中的X
替换为查询条件的字段名。例如,按name字段查询:
List<User> findByName(String name);
同2.1节,下面代码会查询名字为Eric
的所有用户:
List<User> users = userRepository.findByName("Eric");
3.2. StartingWith 和 endingWith
2.2节中,我们基于正则表达式实现了查询以XXX开头
和以XXX结尾
的功能。
下面是自动生成方法的命名规则:
List<User> findByNameStartingWith(String regexp);
List<User> findByNameEndingWith(String regexp);
用法非常简单:
List<User> users = userRepository.findByNameStartingWith("A");
List<User> users = userRepository.findByNameEndingWith("c");
结果和2.2节中的是一样的。
3.3. Between
和2.3节类似,查询年龄介于ageGT
和ageLT
之间的所有用户:
List<User> findByAgeBetween(int ageGT, int ageLT);
用法:
List<User> users = userRepository.findByAgeBetween(20, 50);
3.4. Like 和 OrderBy
让我们看下更高级的用法,组合2种类型的修饰符。
查找名字中包含字母A的用户,并将结果按年龄升序排列:
List<User> users = userRepository.findByNameLikeOrderByAgeAsc("A");
使用2.4节中的数据库,结果为:
[
{
"_id" : ObjectId("55c0e5e5511f0a164a581908"),
"_class" : "org.baeldung.model.User",
"name" : "Antony",
"age" : 33
},
{
"_id" : ObjectId("55c0e5e5511f0a164a581909"),
"_class" : "org.baeldung.model.User",
"name" : "Alice",
"age" : 35
}
]
4. JSON 查询方法
如果利用自动生成的方法或Criteria类不能满足你的需求,我们还可以通过@Query注解,直接编写原生的Mongo JSON查询语句。
4.1. FindBy
find by xxx 例子:
@Query("{ 'name' : ?0 }")
List<User> findUsersByName(String name);
占位符?0
引用方中的第一个参数。
List<User> users = userRepository.findUsersByName("Eric");
4.2 $regex
正则表达式的用法,结果同2.2
和3.2
节内容:
@Query("{ 'name' : { $regex: ?0 } }")
List<User> findUsersByRegexpName(String regexp);
用法:
List<User> users = userRepository.findUsersByRegexpName("^A");
List<User> users = userRepository.findUsersByRegexpName("c$");
4.3. $lt 和 $gt
实现大于、小于查询:
@Query("{ 'age' : { $gt: ?0, $lt: ?1 } }")
List<User> findUsersByAgeBetween(int ageGT, int ageLT);
因为方法有2个参数,我们使用占位符?0
引用第一个参数,?1
引用第二个参数。
List<User> users = userRepository.findUsersByAgeBetween(20, 50);
5. QueryDSL 查询方法
MongoRepository
很好的集成了QueryDSL,因此这里我们也可以利用这款很棒,类型安全的库实现Mongo查询。
5.1. Maven 依赖
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-mongodb</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>5.1.0</version>
</dependency>
5.2. Q-class
QueryDSL依赖Q-class来创建查询(命名规则为Q+实体类名)。但不需要我们手动创建这个类,我们只需安装maven插件,它会自动帮我生成:
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>
org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor
</processor>
</configuration>
</execution>
</executions>
</plugin>
让我们看下User
类,特别注意下@QueryEntity
注解:
@QueryEntity
@Document
public class User {
@Id
private String id;
private String name;
private Integer age;
// standard getters and setters
}
运行mvn process
后,apt插件会自动在target/generated-sources/java/{your package structure}
下生成Q-class:
/**
* QUser is a Querydsl query type for User
*/
@Generated("com.mysema.query.codegen.EntitySerializer")
public class QUser extends EntityPathBase<User> {
private static final long serialVersionUID = ...;
public static final QUser user = new QUser("user");
public final NumberPath<Integer> age = createNumber("age", Integer.class);
public final StringPath id = createString("id");
public final StringPath name = createString("name");
public QUser(String variable) {
super(User.class, forVariable(variable));
}
public QUser(Path<? extends User> path) {
super(path.getType(), path.getMetadata());
}
public QUser(PathMetadata<?> metadata) {
super(User.class, metadata);
}
}
备注:如果您使用的是Eclipse,则引入此插件将在pom中生成以下警告:
You need to run build with JDK or have tools.jar on the classpath. If this occurs during eclipse build make sure you run eclipse under JDK as well (com.mysema.maven:apt-maven-plugin:1.1.3:process:default:generate-sources
快速解决办法是在eclipse.ini
中手动指定JDK:
...
-vm
{path_to_jdk}\jdk{your_version}\bin\javaw.exe
5.3. 新的 Repository
要获得QueryDSL的支持,需要将我们的Repositor继承自QueryDslPredicateExecutor
接口:
public interface UserRepository extends
MongoRepository<User, String>, QuerydslPredicateExecutor<User>
5.4. eq 等于查询
现在使用QueryDSL来实现我们前面的查询:
QUser qUser = new QUser("user");
Predicate predicate = qUser.name.eq("Eric");
List<User> users = (List<User>) userRepository.findAll(predicate);
5.5. StartingWith 和 EndingWith 查询
查找名字以A
开头的用户:
QUser qUser = new QUser("user");
Predicate predicate = qUser.name.startsWith("A");
List<User> users = (List<User>) userRepository.findAll(predicate);
查找名字以c
结尾的用户:
QUser qUser = new QUser("user");
Predicate predicate = qUser.name.endsWith("c");
List<User> users = (List<User>) userRepository.findAll(predicate);
结果同2.2, 3.2, 4.2节中的一样。
5.6. Between 查询
查找年龄介于20到50岁之间的用户:
QUser qUser = new QUser("user");
Predicate predicate = qUser.age.between(20, 50);
List<User> users = (List<User>) userRepository.findAll(predicate);
6. 总结
在本文中,我们探讨了使用Spring Data MongoDB进行查询的多种方法。
文中的示例和代码片段可从GitHub上获取。