我们继续MongoDB系列博客的第三篇,记录下springboot整合MongoDB的基本curd操作,各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!
目录
测试环境准备
测试环境的准备工作就不贴图了,就是创建一个springboot项目,贴一下测试用到的实体类和pom依赖
@Data
@Builder
@Document("user")
public class User {
private String username;
private String sex;
private String address;
private String createTime;
private Integer age;
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>mongo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mongo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- SpringBoot 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mongodb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
测试数据
public User getUser(){
return User.builder().username("zhaoliu").age(10).sex("男").address(
"rizhao").build();
}
public List<User> getUserList(){
User user2 =
User.builder().username("zhangsan").age(10).sex("男").address("jinan").build();
User user3 =
User.builder().username("lisi").age(20).sex("女").address("jinan").build();
List<User> userList = new ArrayList<>();
userList.add(user2);
userList.add(user3);
return userList;
}
public void clearDb(){
mongoTemplate.dropCollection(User.class);
}
测试MongoTemplate的curd
insert 操作
@Test
public void insert(){
clearDb();
// insert one
User user = getUser();
User user1 = mongoTemplate.insert(user);
// insert many
List<User> userList = getUserList();
mongoTemplate.insert(userList,User.class);
//mongoTemplate.insertAll(userList);
}
查看数据库结果如下:
如上,上面我们构建User对象时,并没有为createTime属性赋值,也没有为id属性赋值,在保存后,数据库中并没有createTime, 不同于保存mysql 对象属性不赋值会有默认保存进数据库, 比如这个字符串类型的createTime 会默认为空保存,并且也没有id属性,但是保存时mongoDB会默认生成一个Object类型的_id。
上面我们User试题中并没有id属性,保存后在数据库中默认生成了一个Object类型的_id,看下加上id属性后的效果:
private String id;
同样执行上面的测试方法,执行结果如下:
如上:虽然我们在User对象中加了id属性,但是保存时并没有单独的id字段,id会自动映射成"_id"吗?测试一下:
User user = User.builder().id("qazwsx").username("zhaoliu").age(20).sex("男").address("rizhao").build();
我们为id赋值,再次保存后,发现_id的值为qazwsx,可以确定id属性自动映射成了 _id。 我们不想让id自动映射为_id,就想要数据库中保存id怎么办呢,可以使用@Field显示的指定它对应的属性名。
也可以使用@Field注解指定保存到数据库中字段的名字,比如指定User对象id属性保存到数据库中为uid,可以这样:
@Field("uid")
private String id;
User user2 = User.builder().id("zbc").username("yjh").age(18).build();
mongoTemplate.insert(user2);
执行结果如下:
如果我们既不想使用数据库默认生成的_id,也不想用id自动映射的 _id,可以使用@Id注解标识任意字段为数据库的 _id,比如使用@Id标识username属性,保存后username就会作为数据库的 _id。当然一般我们不会这么办,毕竟username作为主键还是不合适的,这里只是为了测试。
@Id
private String username;
数据库结果如下:
简单总结下保存时的一些细节:
1.保存时mongoDB会默认生成一个Object类型的_id。
2.不初始化属性,保存后数据库没有此filed。
3.执行保存时在java class中的id属性,会自动映射成"_id"。
4.使用@Id标记的字段也会在数据库中显示为_id
接下来的就只贴测试方法不在贴数据库截图了,大家感兴趣可以自己在本地试下:
save:没有则创建,存在则更新
@Test
public void save(){
clearDb();
// id为 610253fe88164b310a41ca2f 的数据age修改为100
User user = User.builder().id("610253fe88164b310a41ca2f").username("zhaoliu").sex("男").age(100).build();
mongoTemplate.save(user);
// 创建testuser集合 并新增一条数据
mongoTemplate.save(user,"testuser");
}
删除操作
@Test
public void delete(){
Query query = new Query();
Criteria criteria = Criteria.where("_id").is("60ec045d0c385469452d1c21");
query.addCriteria(criteria);
mongoTemplate.remove(query,User.class);
Query query1 = new Query(Criteria.where("_id").ne("60ec0c56b0f4dc6363dfc02a"));
mongoTemplate.remove(query1,"testuser");
// delete all datas in collection
mongoTemplate.dropCollection(User.class);
// delete collection
mongoTemplate.dropCollection("mytest1");
}
修改操作
/**
* Update.update(key,value)也可完成修改 ---> 修改一个字段
* Update.update(key,value).set("key,value) ---> 修改两个字段
* Update.update(key,value).set("key,value).set(key,value).set...... ---> 修改多个字段
* 1.修改满足条件的第一条数据 使用updateFirst
* 2.修改满足条件的所有数据 使用updateMulti
* 3.upsert: 按条件查询 查询到数据则修改 没查询到则添加 并且upsert只会更新匹配到的第一条数据
* 如下:若查询到有username为wangwu的数据则将sex改为男
* 若没查询到有username为wangwu的数据则新插入一条数据(username:wangwu sex:男)
*/
@Test
public void update(){
/*ObjectId objectId = new ObjectId("60ee4af0771c712494b9ac4d");
Query query = new Query();
Criteria criteria = Criteria.where("_id").is(objectId);
query.addCriteria(criteria);
Update update = new Update();
update.set("username","zhangsan");*/
Criteria criteria = Criteria.where("username").is("lisi123");
Query query = Query.query(criteria);
Update update = Update.update("username", "lisi");
//mongoTemplate.updateFirst(query,update,User.class);
//mongoTemplate.updateMulti(query,update,User.class);
//mongoTemplate.upsert(query,update,User.class);
Criteria criteria1 = Criteria.where("username").is("wangwu");
Query query1 = Query.query(criteria1);
Update update1 = Update.update("sex","男");
mongoTemplate.upsert(query1,update1,User.class);
}
基本查询、范围查询、模糊查询
需要注意的是范围查询,范围查询需要使用Criteria 的andOperator(Criteria... criteria)方法,比如按照age的范围进行查询时需要这样写:
Criteria criteria3 = new Criteria();
criteria3.andOperator(Criteria.where("age").gt(10),Criteria.where("age").lt(50));
而不能像追加多个条件一样使用如下的方式:
Criteria criteria3 = Criteria.where("age").gt(10).and("age").lt(50); Criteria criteria3 = new Criteria(); criteria3.and("age").gt(10); criteria3.and("age").lt(50);
否则会报如下错误:
InvalidMongoDbApiUsageException: Due to limitations of the com.mongodb.BasicDocument, you can't add a second 'age' expression specified as 'age : Document{{$lt=50}}'. Criteria already contains 'age : Document{{$gt=10}}'.
/**
* 基本查询 模糊查询 范围查询
* 使用regex(Pattern pattern) 实现模糊查询
* 完全匹配:Pattern pattern = Pattern.compile("^张$", Pattern.CASE_INSENSITIVE);
* 右匹配:Pattern pattern = Pattern.compile("^.*张$", Pattern.CASE_INSENSITIVE);
* 左匹配:Pattern pattern = Pattern.compile("^张.*$", Pattern.CASE_INSENSITIVE);
* 模糊匹配:Pattern pattern = Pattern.compile("^.*张.*$", Pattern.CASE_INSENSITIVE);
*/
@Test
public void select(){
Criteria criteria = Criteria.where("username").is("wangwu");
Query query = Query.query(criteria);
List<User> userList = mongoTemplate.find(query, User.class);
System.out.println(userList);
List<User> all = mongoTemplate.findAll(User.class);
System.out.println(all);
// 模糊查询
Pattern pattern = Pattern.compile("^lisi.*$", Pattern.CASE_INSENSITIVE);
Criteria criteria1 = Criteria.where("username").regex(pattern);
Query query1 = Query.query(criteria1);
User user = mongoTemplate.findOne(query1, User.class);
System.out.println(user);
Criteria criteria2 = Criteria.where("_id").is("60ee4af0771c712494b9ac4d");
Query query2 = Query.query(criteria2);
User user1 = mongoTemplate.findOne(query2, User.class);
System.out.println(user1);
User user2 = mongoTemplate.findById("60ee4af0771c712494b9ac4d", User.class);
System.out.println(user2);
long count = mongoTemplate.count(query, User.class);
System.out.println(count);
boolean exists = mongoTemplate.exists(query, User.class);
System.out.println(exists);
// 范围查询
// InvalidMongoDbApiUsageException: Due to limitations of the com.mongodb.BasicDocument, you can't add a second 'age' expression specified as 'age : Document{{$lt=50}}'. Criteria already contains 'age : Document{{$gt=10}}'.
// Criteria criteria3 = Criteria.where("age").gt(10).and("age").lt(50);
// Criteria criteria3 = new Criteria(); criteria3.and("age").gt(10); criteria3.and("age").lt(50);
Criteria criteria3 = new Criteria();
criteria3.andOperator(Criteria.where("age").gt(10),Criteria.where("age").lt(50));
Query query3 = Query.query(criteria3);
List<User> userList1 = mongoTemplate.find(query3, User.class);
System.out.println(userList1);
// 查询指Map定collection中的数据
Criteria criteria10 = Criteria.where("name").is("zhangsan");
Query query10 = Query.query(criteria10);
List<Map> testuser = mongoTemplate.find(query10, Map.class,"testuser");
System.out.println(testuser);
}
排序
/**
* 排序
* 1.默认升序 Sort.by("age")
* 2.可通过Sort.by(Sort.Order.desc("age")) 或 Sort.by(Sort.Direction.DESC,"age") 指定排序方式
* 3.
*/
@Test
public void select1(){
Query query = new Query();
Sort sort = Sort.by("age");
query.with(sort);
List<User> userList = mongoTemplate.find(query, User.class);
System.out.println(userList);
Sort sort1 = Sort.by(Sort.Order.desc("age"));
query.with(sort1);
List<User> userList1 = mongoTemplate.find(query, User.class);
System.out.println(userList1);
Sort sort2 = Sort.by(Sort.Direction.DESC,"age");
query.with(sort2);
List<User> userList2 = mongoTemplate.find(query, User.class);
System.out.println(userList2);
}
分页查询
@Test
public void select2(){
Pageable page = PageRequest.of(0, 2);
Query query = new Query();
query.with(page);
List<User> userList = mongoTemplate.find(query, User.class);
System.out.println(userList + "---" + userList.size());
Sort sort = Sort.by("age");
Pageable pageable = PageRequest.of(0,2,sort);
Query query1 = new Query();
query1.with(pageable);
List<User> userList1 = mongoTemplate.find(query1, User.class);
System.out.println(userList1);
Pageable pageable1 = PageRequest.of(0, 2, Sort.Direction.DESC, new String[]{"age"});
Query query2 = new Query();
query2.with(pageable1);
List<User> userList2 = mongoTemplate.find(query2, User.class);
System.out.println(userList2);
// 分页1
long count = mongoTemplate.count(new Query(), User.class);
System.out.println(count);
Page<User> userPage = new PageImpl<User>(userList2,pageable1,count);
System.out.println("---getTotalElements---" + userPage.getTotalElements());// 总记录数
System.out.println("---getTotalPages---" + userPage.getTotalPages());// 总页数
System.out.println("---getNumber---" + userPage.getNumber());// 当前页
System.out.println("---getSize---" + userPage.getSize());// 每页大小
System.out.println("---getContent---" + userPage.getContent()); // 数据
System.out.println("---getNumberOfElements---" + userPage.getNumberOfElements());// 当前页的元素数
// 分页2
Page<User> userPage1 = PageableExecutionUtils.getPage(userList2, pageable1, () -> count);
System.out.println("---getTotalElements---" + userPage1.getTotalElements());// 总记录数
System.out.println("---getTotalPages---" + userPage1.getTotalPages());// 总页数
System.out.println("---getNumber---" + userPage1.getNumber());// 当前页
System.out.println("---getSize---" + userPage1.getSize());// 每页大小
System.out.println("---getContent---" + userPage1.getContent()); // 数据
System.out.println("---getNumberOfElements---" + userPage1.getNumberOfElements());// 当前页的元素数
}
管道Aggregation查询
/**
* AggregationResults<T> 一般用查询的类接就好
* [Document{{_id=60ee4af0771c712494b9ac4d, age=30.0}}, Document{{_id=60ee9cd8d82d3d76d2ed2204, age=30.0}}]
* [User(id=60ee4af0771c712494b9ac4d, username=null, password=null, sex=null, address=null, createTime=null, age=30), User(id=60ee9cd8d82d3d76d2ed2204, username=null, password=null, sex=null, address=null, createTime=null, age=30)]
*/
@Test
public void aggregation(){
// select * from user where age > 20
Criteria criteria = Criteria.where("age").gt(20);
// 使用List<AggregationOperation>封装查询条件 或者直接用Aggregation构建 如:Aggregation.match(criteria)
List<AggregationOperation> operations = new ArrayList<>();
operations.add(Aggregation.match(criteria));
TypedAggregation<User> aggregation = Aggregation.newAggregation(User.class,operations);
AggregationResults<Document> aggregationResults =
mongoTemplate.aggregate(aggregation, Document.class);
System.out.println(aggregationResults.getMappedResults());
// select age,count(*) from user group by age
List<AggregationOperation> operations1 = new ArrayList<>();
operations1.add(Aggregation.group("age").sum("age").as("counts"));
TypedAggregation<User> typedAggregation = TypedAggregation.newAggregation(User.class, operations1);
AggregationResults<User> aggregationResults1 = mongoTemplate.aggregate(typedAggregation,
User.class);
System.out.println(aggregationResults1.getMappedResults());
Set<String> names = new HashSet<String>();
names.add("lisi");
names.add("wangwu");
Aggregation agg = Aggregation.newAggregation(
Aggregation.match(Criteria.where("username").in(names)),
Aggregation.match(Criteria.where("age").gt(10))
);
AggregationResults<User> aggregationResults2 = mongoTemplate.aggregate(agg, "user", User.class);
System.out.println(aggregationResults2.getMappedResults());
}
管道Aggregation分页查询
@Test
public void aggregation1(){
int pageNum = 0;
int pageSize = 2;
Pageable pageable = PageRequest.of(pageNum,pageSize);
int totalCount;
// 查询条件
List<AggregationOperation> operations = new ArrayList<>();
// 模拟一下带条件的查询
String username = "zhaoliu";
if(StringUtils.isNotBlank(username)){
operations.add(Aggregation.match(Criteria.where("username").ne("zhaoliu")));
Aggregation aggregation = Aggregation.newAggregation(operations);
AggregationResults<User> aggregationResults = mongoTemplate.aggregate(aggregation, "user",
User.class);
totalCount = aggregationResults.getMappedResults().size();
}else{
totalCount = mongoTemplate.findAll(User.class).size();
}
// 分页信息
operations.add(Aggregation.skip((long)pageNum * pageSize));
operations.add(Aggregation.limit(pageSize));
// 排序 或者在Pageable中添加排序
operations.add(Aggregation.sort(Sort.Direction.DESC, "age"));
//当前页数据
Aggregation aggregation = Aggregation.newAggregation(operations);
AggregationResults<User> results = mongoTemplate.aggregate(aggregation, "user", User.class);
// 分页查询
Page<User> userPage = PageableExecutionUtils.getPage(results.getMappedResults(), pageable,
() -> totalCount);
System.out.println("---getTotalElements---" + userPage.getTotalElements());// 总记录数
System.out.println("---getTotalPages---" + userPage.getTotalPages());// 总页数
System.out.println("---getNumber---" + userPage.getNumber());// 当前页
System.out.println("---getSize---" + userPage.getSize());// 每页大小
System.out.println("---getContent---" + userPage.getContent()); // 数据
System.out.println("---getNumberOfElements---" + userPage.getNumberOfElements());// 当前页的元素数
}
通过BasicQuery指定返回的字段
/**
* 通过BasicQuery指定返回的字段
* 不设置的属性字段值为null
* 如:[User(id=60ee4af0771c712494b9ac4d, username=lisi, sex=女, address=null, createTime=null, age=20)]
*/
@Test
public void select3(){
// 查询条件
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("username", "lisi");
//指定返回的字段
BasicDBObject fieldsObject = new BasicDBObject();
fieldsObject.put("username",true);
fieldsObject.put("age",true);
fieldsObject.put("sex",true);
Query query = new BasicQuery(dbObject.toJson(),fieldsObject.toJson());
List<User> userList = mongoTemplate.find(query, User.class);
System.out.println(userList);
}
通过Aggregation.project指定返回的字段
/**
* 通过Aggregation.project指定返回的字段
* project :控制返回的字段,例如一个实体类,我们只需要部分字段
* 同上面一样通过对象接收结果时,没设置的属性值为null
* 如:[User(id=60ee4af0771c712494b9ac4d, username=lisi, password=null, sex=女, address=null, createTime=null, age=20)]
* 我们可以使用Map接收
* 如:[{_id=60ee4af0771c712494b9ac4d, username=lisi, sex=女, age=20.0}]
* 综上两种方式,如果只想返回指定字段,可以使用map类型来接收返回结果,但是也要看返回数据的结构是否利于后续解析
* 如果结构复杂,用对象的方式会方便些,虽然有一些我们不需要的属性,但是在最终接口返回前可以转换一下去掉不需要的属性
*/
@Test
public void select4(){
Criteria criteria = Criteria.where("username").is("lisi");
MatchOperation match = Aggregation.match(criteria);
ProjectionOperation project = Aggregation.project("username","age","sex");
Aggregation aggregation = Aggregation.newAggregation(match,project);
AggregationResults<User> results = mongoTemplate.aggregate(aggregation, "user", User.class);
System.out.println(results.getMappedResults());
AggregationResults<Map> results1 = mongoTemplate.aggregate(aggregation, "user", Map.class);
System.out.println(results1.getMappedResults());
}