本文代码示例参见:https://gitee.com/imlichao/MongoDB-example
我们使用Spring Data MongoDB可以方便的在Spring boot项目中操作MongoDB
文档地址:https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-mongodb
加载包
在pom文件中加入spring-boot-starter-data-mongodb配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
配置参数
# user - 用户名、secret - 密码、mongo1.example.com - 数据库地址、27017- 端口号、teat - 库名
spring.data.mongodb.uri=mongodb://user:[email protected]:27017/test
实体映射
我们可以通过实体类来进行关系映射,从而能够方便的使用对象进行数据操作。
Spring Data MongoDB提供了一些方便的映射注释供我们使用,下面列出官方说明地址以供参考
官方说明文档:https://docs.spring.io/spring-data/mongodb/docs/2.1.3.RELEASE/reference/html/#mapping-usage
-
@Id:用于标记id字段,没有标记此字段的实体也会自动生成id字段,但是我们无法通过实体来获取id。id建议使用ObjectId类型来创建。
-
@Document:用于标记此实体类是mongodb集合映射类。可以使用collection参数指定集合名称。特别需要注意的是如果实体类没有为任何字段创建索引将不会自动创建集合。
-
@Indexed:用于标记为某一字段创建索引。direction参数可以指定排序方向,升或降序。
-
@CompoundIndex:用于创建复合索引。def参数可以定义复合索引的字段及排序方向。
-
@Transient:被该注解标注的,将不会被录入到数据库中。只作为普通的javaBean属性。
-
@PersistenceConstructor:用于声明构造函数,作用是把从数据库取出的数据实例化为对象。
-
@Field:用于指定某一个字段映射到数据库中的名称。
-
@DBRef:用于指定与其他集合的级联关系,但是需要注意的是并不会自动创建级联集合。
一个简单的例子:
//注释此类将被映射到数据库的一个集合(collection为集合名称)
@Document(collection = "ex_entity_test")
//创建联合索引
@CompoundIndexes({
//联合索引 name 索引名称 、def 索引字段、parameter1升序、parameter3降序
@CompoundIndex(name = "compound_index", def = "{'parameter1': 1, 'parameter3': -1}")
})
public class EntityTest implements Serializable {
//标记id字段
@Id
private ObjectId id;
//创建单字段索引(默认ASCENDING 升序、DESCENDING 降序)
@Indexed(direction = DESCENDING)
private Long parameter1;
//修改映射到数据库中的名称
@Field("parameter2_")
private String parameter2;
private Date parameter3;
private Integer parameter4;
//关联其他集合(不添加此注释时List将会保存具体的实体值,而添加了此注释List保存的是关联集合的id)
@DBRef
private List<EntityTest1> parameter5;
//此字段不映射到数据库
@Transient
private Integer parameter6;
public EntityTest(){
}
//声明构造函数,用于实例化查询结果数据
@PersistenceConstructor
public EntityTest(Long parameter1, String parameter2, Date parameter3, Integer parameter4, List<EntityTest1> parameter5) {
this.parameter1 = parameter1;
this.parameter2 = parameter2;
this.parameter3 = parameter3;
this.parameter4 = parameter4;
this.parameter5 = parameter5;
}
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
......
public Integer getParameter6() {
return parameter6;
}
public void setParameter6(Integer parameter6) {
this.parameter6 = parameter6;
}
}
上例关联的集合
//注释此类将被映射到数据库的一个集合(collection为集合名称)
@Document(collection = "ex_entity_test1")
public class EntityTest1 implements Serializable {
//标记id字段
@Id
private ObjectId id;
//如果实体类没有为任何字段创建索引将不会自动创建集合
@Indexed
private Long parameter1;
public EntityTest1(){
}
public EntityTest1(Long parameter1) {
this.parameter1 = parameter1;
}
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public Long getParameter1() {
return parameter1;
}
public void setParameter1(Long parameter1) {
this.parameter1 = parameter1;
}
}
数据操作
当完成一系列的配置与准备工作后,我们就可以使用MongoTemplate来操作数据库了。
MongoTemplate为我们提供了全面的增删改查等操作数据库的方法,详情可查看官方说明文档和API文档
官方说明文档:https://docs.spring.io/spring-data/mongodb/docs/2.1.3.RELEASE/reference/html/#mongo-template
API文档:https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/MongoTemplate.html
一个简单的例子:
@Controller
public class MongoController {
@Autowired
private MongoTemplate mongoTemplate;
@GetMapping(value = "/")
public String index(){
return "redirect:/find";
}
//新增文档
@GetMapping(value = "/insert")
public String insert(){
//insert方法并不提供级联类的保存,所以级联类需要先自己先保存
EntityTest1 entityTest1_1 = new EntityTest1(1000L);
//执行完insert后对象entityTest1将会获得保存后的id
mongoTemplate.insert(entityTest1_1);
//再添加一条
EntityTest1 entityTest1_2 = new EntityTest1(1001L);
mongoTemplate.insert(entityTest1_2);
//创建列表并将保存后的关联对象添加进去
ArrayList<EntityTest1> entityTest1List = new ArrayList<EntityTest1>();
entityTest1List.add(entityTest1_1);
entityTest1List.add(entityTest1_2);
//新增主体对象
EntityTest entityTest = new EntityTest(100L,"test",new Date(),10,entityTest1List);
//新增数据的主键已经存在,则会抛DuplicateKeyException异常
mongoTemplate.insert(entityTest);
return "redirect:/find";
}
//保存文档
//保存与新增的主要区别在于,如果主键已经存在,新增抛出异常,保存修改数据
@GetMapping(value = "/save")
public String save(){
//查询最后一条数据并更新
Sort sort = new Sort(Sort.Direction.DESC,"parameter3");
EntityTest entityTest = mongoTemplate.findOne(Query.query(Criteria.where("")).with(sort),EntityTest.class);
entityTest.setParameter4(3000);
//保存数据的主键已经存在,则会对已经存在的数据修改
mongoTemplate.save(entityTest);
return "redirect:/find";
}
//删除文档
@GetMapping(value = "/delete")
public String delete(){
//查询第一条数据并删除
EntityTest entityTest = mongoTemplate.findOne(Query.query(Criteria.where("")),EntityTest.class);
//remove方法不支持级联删除所以要单独删除子数据
List<EntityTest1> entityTest1List = entityTest.getParameter5();
for(EntityTest1 entityTest1:entityTest1List){
mongoTemplate.remove(entityTest1);
}
//删除主数据
mongoTemplate.remove(entityTest);
return "redirect:/find";
}
//更新文档
@GetMapping(value = "/update")
public String update(){
//将查询条件符合的全部文档更新
Query query = new Query();
Update update = Update.update("parameter2_","update");
mongoTemplate.updateMulti(query,update,EntityTest.class);
return "redirect:/find";
}
//查询文档
@GetMapping(value = "/find")
public String find(Model model){
//查询小于当前时间的数据,并按时间倒序排列
Sort sort = new Sort(Sort.Direction.DESC,"parameter3");
List<EntityTest> findTestList = mongoTemplate.find(Query.query(Criteria.where("parameter3").lt(new Date())).with(sort) ,EntityTest.class);
model.addAttribute("findTestList",findTestList);
//使用findOne查询如果结果极为多条,则返回排序在最上面的一条
EntityTest findOneTest = mongoTemplate.findOne(Query.query(Criteria.where("parameter3").lt(new Date())).with(sort) ,EntityTest.class);
model.addAttribute("findOneTest",findOneTest);
//模糊查询
List<EntityTest> findTestList1 = mongoTemplate.find(Query.query(Criteria.where("parameter3").lt(new Date()).and("parameter2").regex("es")) ,EntityTest.class);
model.addAttribute("findTestList1",findTestList1);
//分页查询(每页3行第2页)
Pageable pageable = new PageRequest(1,3,sort);
List<EntityTest> findTestList2 = mongoTemplate.find(Query.query(Criteria.where("parameter3").lt(new Date())).with(pageable) ,EntityTest.class);
//共多少条
Long count = mongoTemplate.count(Query.query(Criteria.where("parameter3").lt(new Date())),EntityTest.class);
//返回分页对象
Page<EntityTest> page = new PageImpl<EntityTest>(findTestList2,pageable,count);
model.addAttribute("page",page);
//分页查询(通过起始行和数量也可以自己实现分页逻辑)
List<EntityTest> findTestList3 = mongoTemplate.find(Query.query(Criteria.where("parameter3").lt(new Date())).with(sort).skip(3).limit(3) ,EntityTest.class);
model.addAttribute("findTestList3",findTestList3);
return "/index";
}
}
查询
由于查询相对于其他增删改的操作要复杂一些,所以在这里单独说一下查询。还看上面的例子就可以。
首先我们先介绍一下几个和查询有关的类Query、Criteria、Sort、PageRequest、PageImpl
Query
查询对象定义一个查询的所有要素,其中包括筛选条件、排序、起始行、返回条数等内容
API文档:https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/query/Query.html
常用方法介绍:
Criteria
查询条件类,使用此类定义查询时的查询条件相当于SQL的where。
API文档:https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/query/Criteria.html
常用方法介绍:
Sort
查询排序类,使用此类可以创建查询排序。
API文档:https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Sort.html?is-external=true
常用方法介绍:
PageRequest和PageImpl
这两个类都是分页相关的封装类,与其他数据库的使用方法一样。PageRequest分页请求类,PageImpl为页面封装类。
API文档:https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/PageRequest.html
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/PageImpl.html