概述
java中集成mongo的方法在上文已经详细阐述了,接下来就是API的具体使用方法
规范的使用方式是,对外封装好CRUD方法,内部通过mongoTemplate实现,具体对外提供的方法根据业务而定,但是最好不要直接使用mongoTemplate操作,当然这是在基于规范代码的条件下
mongoTemplate中封装了极为丰富的API方法,可以满足基本上所有的mongo命令操作,接下来开始逐个介绍
常用标签
@Id
用于标注在属性上,声明该字段为mongo文档的主键ID。当然如果不使用该标签,那么mongo会默认选择名称为ID的属性为mongo中的主键ID。如果自己不设置@Id主键,mongo会自动生成一个唯一主键,并且插入时效率远高于自己设置主键。
在实际业务中不建议自己设置主键,应交给mongo自己生成,自己可以设置一个业务id,如int型字段,用自己设置的业务id来维护相关联的表。
总结来讲,不建议自己生成主键ID,使用mongo自动生成主键ID最好,但是自己可以指定另一个属性为业务主键ID,因为通常来说,也不建议主键ID具有任何业务属性
@Document
用于标注在实体类上,通过collection属性声明该对象对应的集合
@Indexed
用于标注在属性上,为该属性创建索引。同时可指定该索引的唯一性,稀疏性,部分性,以及TTL
目前更推荐,在mongo命令行或通过mongo.indexOps创建索引
@CompoundIndex
用于标注在实体类上,声明创建该实体类对应集合的各种索引,目前更推荐,在mongo命令行或通过mongo.indexOps创建索引
具体用法
// 创建2dsphere球面索引
@CompoundIndex(name = "location_index",def = "{'location'}:{'2dsphere'}")
//创建复合索引
@CompoundIndexes({
@CompoundIndex(name = "id_name_idx", def = "{'id': 1, 'name': 1}", unique = true)
})
@Field
用于标注在属性上,声明属性存储在mongo中的列名,可以不加,不加的话默认以参数名为列名
@Transient
用于标注在属性上,不会被录入到数据库中,也不在查询或聚合后映射到指定的属性上,只作为普通的javaBean属性。
@DBRef
关联另一个document对象。类似于mysql的表关联,但并不一样,mongo不会做级联的操作。
目前还没有用到这个操作
插入操作
与命令一样,增加操作也包括insert与save两种,下面介绍javaAPi使用方法
insert对象,指定ID
- 定义对象实体类 ```java import lombok.Data; import org.springframework.data.mongodb.core.mapping.Document;
/**
- @author 冯铁城 [17615007230@163.com]
- @date 2022-07-20 19:12:01
- @describe: MongoDB实体类
“@Document(collection = “student”)” 用于指定对应哪个集合 */ @Data @Document(collection = “student”) public class Student {
private String id;
private String name;
private int age; } ```
- 新增 ```java import cn.hutool.core.lang.Assert; import cn.hutool.json.JSONUtil; import com.ftc.mongotest.entity.Student; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Query;
/**
- @author 冯铁城 [17615007230@163.com]
- @date 2022-07-23 19:02:32
@describe: 增删改查测试 */ @SpringBootTest public class TestCrud {
@Autowired private MongoTemplate mongoTemplate;
@Test void testInsert() {
//1.指定_id字段
Student student = new Student();
student.setId("1");
student.setName("ftc");
student.setAge(18);
mongoTemplate.insert(student);
//2.校验
Student result = mongoTemplate.findOne(new Query(), Student.class);
Assert.isTrue(JSONUtil.toJsonStr(student).endsWith(JSONUtil.toJsonStr(result)));
} }
<a name="gAwwx"></a>
### insert对象,不指定ID
**需要注意的是,mongo自动生成的id是24位的字符,因此最好用String作为id的属性,不然会出现主键ID设置失败问题,进而引发主键ID重复!!!!!!!!!!!!!!!!**
```java
@Test
void testInsert() {
//1.指定_id字段
Student student = new Student();
student.setName("ftc");
student.setAge(18);
Student insert = mongoTemplate.insert(student);
Console.log(insert);
//2.校验
Student result = mongoTemplate.findOne(new Query(), Student.class);
Assert.notBlank(student.getId());
Assert.isTrue(student.getName().equals(result.getName()));
Assert.isTrue(student.getAge() == result.getAge());
}
insert对象,指定集合
如果insert的对象通过标签指定集合,同时也没有在insert时指定集合,那么会创建一个该对象类名称的一个集合来存储该对象
@Test
void testInsert() {
//1.指定_id字段
JSONObject obj = new JSONObject();
obj.set("type", "自定义类型");
obj = mongoTemplate.insert(obj, "student");
Console.log(obj);
}
save对象
save操作同样可以指定集合名称等,这里不做过多累述。只需知道save方法会覆盖更新即可
@Test
void testSave() {
//1.保存
Student student = new Student();
student.setId("1");
student.setName("ftc");
student.setAge(18);
mongoTemplate.save(student);
//2.校验
Student result = mongoTemplate.findOne(new Query(), Student.class);
Assert.isTrue("ftc".equals(result.getName()));
//3.覆盖保存(更新)
student.setName("马冬梅");
mongoTemplate.save(student);
//4.校验
result = mongoTemplate.findOne(new Query(), Student.class);
Assert.isTrue("马冬梅".equals(result.getName()));
}
删除操作
删除操作通过remove()方法实现,可通过实体类删除对象、通过条件进行删除,也可全量删除,同样支持指定集合名称进行指定删除
@Test
void testDelete() {
//1.保存测试数据
Student student = null;
for (int i = 1; i <= 10; i++) {
student = new Student();
student.setId(i + "");
student.setName(i + "ftc");
student.setAge(18);
mongoTemplate.insert(student);
}
//2.验证数据存入
long count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(10 == count);
//3.按条件删除,删除id=1
DeleteResult remove = mongoTemplate.remove(new Query(Criteria.where("id").is("1")), Student.class);
Assert.isTrue(1 == remove.getDeletedCount());
Assert.isTrue(ObjectUtil.isNull(mongoTemplate.findById("1", Student.class)));
//4.直接传入对象根据ID删除,删除id=10
remove = mongoTemplate.remove(student);
Assert.isTrue(1 == remove.getDeletedCount());
Assert.isTrue(ObjectUtil.isNull(mongoTemplate.findById("10", Student.class)));
//5.全量删除
ExecutableRemoveOperation.ExecutableRemove<Student> removeAll = mongoTemplate.remove(Student.class);
Assert.isTrue(8 == removeAll.all().getDeletedCount());
//6.验证数据全部删除
count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(0 == count);
}
修改操作
基于Update更新
与命令一致,需要指定集合名称,条件,以及更新字段和更新属性(multi等)
@Test
void testUpdate() {
//1.保存测试数据
Student student = null;
for (int i = 1; i <= 10; i++) {
student = new Student();
student.setId(i + "");
student.setName(i + "ftc");
student.setAge(18);
mongoTemplate.insert(student);
}
//2.验证数据存入
long count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(10 == count);
//3.按条件更新一条
Query query = new Query(Criteria.where("id").is("1"));
Update update = new Update();
update.set("name", "夏洛");
UpdateResult updateResult = mongoTemplate.updateFirst(query, update, Student.class);
Assert.isTrue(1 == updateResult.getModifiedCount());
Student result = mongoTemplate.findOne(query, Student.class);
Assert.isTrue("夏洛".equals(result.getName()));
//4.按条件更新全部
query = new Query();
update = new Update();
update.set("name", "马冬梅");
updateResult = mongoTemplate.updateMulti(query, update, Student.class);
Assert.isTrue(10 == updateResult.getModifiedCount());
List<Student> students = mongoTemplate.find(query, Student.class);
students.forEach(s -> Assert.isTrue("马冬梅".equals(s.getName())));
//5.另一种更新方式的写法
update = new Update();
update.set("name", "大傻春");
ExecutableUpdateOperation.TerminatingUpdate<Student> apply = mongoTemplate.update(Student.class).apply(update);
Assert.isTrue(10 == apply.all().getModifiedCount());
students = mongoTemplate.find(query, Student.class);
students.forEach(s -> Assert.isTrue("大傻春".equals(s.getName())));
}
基于Save的操作
因为save操作目前不支持批量操作API,所以如果单条数据更新,使用save操作是可以的,批量操作还是使用update比较好
@Test
void testUpdate() {
//1.保存测试数据
for (int i = 1; i <= 10; i++) {
Student student = new Student();
student.setId(i + "");
student.setName(i + "ftc");
student.setAge(18);
mongoTemplate.insert(student);
}
//2.验证数据存入
long count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(10 == count);
//3.查询数据并修改属性
List<Student> students = mongoTemplate.find(new Query(), Student.class);
students.forEach(s -> s.setName("马冬梅"));
//4.批量更新
students.forEach(s -> mongoTemplate.save(s));
//5.验证
students = mongoTemplate.find(new Query(), Student.class);
students.forEach(s -> Assert.isTrue("马冬梅".equals(s.getName())));
}
原子性更新操作
在引入wiredTiger引擎后,mongo实现了对单文档操作的原子性
在javaApi中,通过findAndModify方法进行文档的原子性更新
需要注意的是,该方法只会操作一个文档!!!!!因此如果query的结果集为多个文档,那么最终也只会操作结果集的第一个文档!!!!!@Test
void testUpdate() {
//1.保存测试数据
for (int i = 1; i <= 10; i++) {
Student student = new Student();
student.setId(i + "");
student.setName(i + "ftc");
student.setAge(i);
mongoTemplate.insert(student);
}
//2.验证数据存入
long count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(10 == count);
//3.定义更新属性
Query query = new Query(Criteria.where("age").gt(5));
Update update = new Update();
update.set("name", "马冬梅");
//4.查询数据并修改属性
Student andModify = mongoTemplate.findAndModify(query, update, Student.class);
//5.验证只有一条数据被修改
andModify = mongoTemplate.findById(andModify.getId(), Student.class);
Assert.isTrue("马冬梅".equals(andModify.getName()));
//6.验证其他数据没有被修改
List<Student> students = mongoTemplate.find(new Query(Criteria.where("id").ne(andModify.getId())), Student.class);
students.forEach(s -> Assert.isTrue(!"马冬梅".equals(s.getName())));
}
查询操作
基础的查询操作
包括根据ID查询,查询单条数据,查询批量数据,集合全量查询,去重查询等
需要注意的是,去重查询必须指定去重字段,并且返回的结果集只能是指定的字段@Test
void testSelect() {
//1.保存测试数据
for (int i = 1; i <= 10; i++) {
Student student = new Student();
student.setId(i);
student.setName(i + "ftc");
student.setAge(i);
mongoTemplate.insert(student);
}
//2.验证数据存入
long count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(10 == count);
//3.根据ID查询单个数据
Student result = mongoTemplate.findById(1, Student.class);
Assert.isTrue(1 == result.getAge());
//4.根据条件查询单个数据
result = mongoTemplate.findOne(new Query(Criteria.where("id").is(2)), Student.class);
Assert.isTrue(2 == result.getAge());
//5.根据条件查询批量数据
List<Student> results = mongoTemplate.find(new Query(Criteria.where("id").gt(5)), Student.class);
Assert.isTrue(5 == results.size());
//6.集合全量查询
results = mongoTemplate.findAll(Student.class);
Assert.isTrue(10 == results.size());
//7.新增一条重复age数据
Student repeatStudent = new Student();
repeatStudent.setId(11);
repeatStudent.setAge(1);
repeatStudent.setName("ftccccc");
mongoTemplate.insert(repeatStudent);
count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(11 == count);
//8.去重查询
List<Integer> ages = mongoTemplate.findDistinct(new Query(), "age", Student.class, Integer.class);
Assert.isTrue(10 == ages.size());
}
条件查询
@Test
void testSelect() {
//1.保存测试数据
for (int i = 1; i <= 10; i++) {
Student student = new Student();
student.setId(i);
student.setName(i + "ftc");
student.setAge(i);
mongoTemplate.insert(student);
}
//2.验证数据存入
long count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(10 == count);
//3.等值查询
Query query = new Query(Criteria.where("id").is(1));
List<Student> students = mongoTemplate.find(query, Student.class);
Assert.isTrue(1 == students.size());
Assert.isTrue(1 == students.get(0).getAge());
//4.小于查询
query = new Query(Criteria.where("age").lt(5));
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(4 == students.size());
//5.小于等于查询
query = new Query(Criteria.where("age").lte(5));
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(5 == students.size());
//6.大于查询
query = new Query(Criteria.where("age").gt(7));
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(3 == students.size());
//7.大于等于查询
query = new Query(Criteria.where("age").gte(7));
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(4 == students.size());
//8.不等于查询
query = new Query(Criteria.where("id").ne(5));
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(9 == students.size());
//9.in查询
query = new Query(Criteria.where("age").in(1, 2, 3));
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(3 == students.size());
//10.not in查询
query = new Query(Criteria.where("age").nin(CollUtil.newArrayList(1, 2, 3)));
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(7 == students.size());
//11.and查询
query = new Query(Criteria.where("id").is(1).and("age").is(2));
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(0 == students.size());
//12.or查询
query = new Query(new Criteria().orOperator(
Criteria.where("id").is(2), Criteria.where("id").is(3))
);
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(2 == students.size());
//13.and与or结合查询
query = new Query(Criteria.where("id").is(2).orOperator(
Criteria.where("age").is(2),
Criteria.where("age").is(3)
));
students = mongoTemplate.find(query, Student.class);
Assert.isTrue(1 == students.size());
}
原子性查询操作
包括批量查询并删除、查找并删除、查询并修改、查询并替换
需要注意的是,非批量原子操作,即使根据query遍历的结果集为多条,也只是默认原子性操作结果集的第一条数据!!!!!@Test
void testSelect() {
//1.保存测试数据
for (int i = 1; i <= 10; i++) {
Student student = new Student();
student.setId(i);
student.setName(i + "ftc");
student.setAge(i);
mongoTemplate.insert(student);
}
//2.验证数据存入
long count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(10 == count);
//3.原子性查询并删除批量
Query query = new Query(Criteria.where("id").is(1));
List<Student> students = mongoTemplate.findAllAndRemove(query, Student.class);
Assert.isTrue(1 == students.size());
count = mongoTemplate.count(new Query(), Student.class);
Assert.isTrue(9 == count);
//4.原子性查询并修改
query = new Query(Criteria.where("id").is(2));
Update update = new Update();
update.set("name", "马冬梅");
mongoTemplate.findAndModify(query, update, Student.class);
Student student = mongoTemplate.findOne(query, Student.class);
Assert.isTrue("马冬梅".equals(student.getName()));
//5.原子性查询并删除
query = new Query(Criteria.where("id").is(3));
Student andRemove = mongoTemplate.findAndRemove(query, Student.class);
student = mongoTemplate.findOne(query, Student.class);
Assert.isNull(student);
//6.原子性查询并替换文档
Student studentReplace = new Student();
studentReplace.setId(4);
studentReplace.setAge(100);
studentReplace.setName("替换对象");
query = new Query(Criteria.where("id").is(4));
mongoTemplate.findAndReplace(query, studentReplace, "student");
Student result = mongoTemplate.findOne(query, Student.class);
Assert.isTrue(100 == result.getAge());
}