文章前面
关于版本
依赖 | 版本 |
springboot | 2.0.8.RELEASE |
mongodb | 4.0.14 |
本内容只是为了介绍mongodb最基础的使用以及配置,作为一个知名的数据库,其存在相当多的高级用法,展开来介绍内容会相当多,当然本人并非相关领域的大神,下面内容只不过整理了自己日常使用的一些积累。是对自己经验的积累,也希望能帮助后来的同学
关于项目
本内容也是我尝试整理工作中接触过各种工具在springboot中使用的方法。下面介绍的所有方法都已经提供了测试用例。因为每个例子涉及代码较多,所以文章中只贴出了部分代码。全部的代码在这里:https://gitee.com/daifyutils/springboot-samples。
MongoDB表(集合)的定义
在MySQL中存储一类数据的集合体被叫做表(table),而在mongodb中被叫做集合(collections)。而mongodb中每一条数据被称为文档(document)。
于传统的MySQL需要提前在数据库中完成表创建不同,使用Spring Boot操作mongodb的时候不需要再数据库中创建集合结构,而通过将实体定义为mongodb实体来完成集合的创建。
定义实体需要的注解
Spring Data为了定义mongodb的集合结构,提供了下面几个注解
注解 | 说明 |
@Document | 标识当前实体是一个mongodb实体 |
@Id | 标识当前字段为主键,需要注意的是此注解只能存在一个 |
@Indexed | 标识当前字段需要添加索引,添加索引后的字段,在进行查询操作的时候会提高速度 |
@CompoundIndex | 标识一个联合索引 |
@Field | 对当前字段的额外内容进行定义,主要是用来定义集合中字段实际名称 |
@Transient | 标识此字段为java属性而非mongodb字段 |
@DBRef | 将此字段同另外一个mongodb的文档进行关联 |
注解的使用
注解的使用
现在创建下面两个实体
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.CompoundIndexes;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
@Data
@Document(collection = "Province")
@CompoundIndexes({
@CompoundIndex(name = "userName_age_idx", def = "{'provinceName': 1, 'provinceCode': -1}")
})
public class Province {
@Id
private String id;
private String provinceName;
private String provinceCode;
@DBRef
private List<City> cities;
}
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Data
@Document(collection = "City")
public class City {
@Id
private String id;
private String cityName;
@Indexed
@Field(value = "code")
private String cityCode;
@Transient
private String cityRemark;
}
关于上面的例子中使用的注解作用:
@Document
上面例子中使用了@Document定义了两个数据集合Province
和City
。
@Id
数据集合Province
和City
中id字段被定义为主键。
@Indexed
数据集合City
中将cityCode字段添加了索引。
@Field(value = “code”)
在定义mongodb字段的时候,不添加@Field
的注解时保存到集合中的字段名和实体中的属性名保持一致,而数据集合City
中cityCode字段添加了上面注解后实际保存到集合中的字段会被设置为code
@Transient
此字段标识的cityRemark
只是作为java属性来使用,在将数据保存至mongodb的时候此内容并不会保存下来。
@DBRef
将此字段和另外一个mongodb文档进行关联,添加此注解和不添加此注解的主要区别在于。当不添加此注解的时候,此字段中的City
内容会作为Province
文档的一部分保存起来,而当添加此注解后Province
文档中只会维持City
的主键值。
当我们创建一些Province
和City
数据后可以看到Province
中数据内容如下:
@CompoundIndex
将provinceName
和provinceCode
字段作为一个联合索引。数字参数指定索引的方向,1为正序,-1为倒序。这里需要注意在使用@Indexed
标识索引的时候,创建集合后会自动创建对应的索引,而使用@CompoundIndex
标识索引的时候是在新增数据之后关联索引才产生。
mongodb简单的CRUD
通过Spring Data操作mongodb主要是使用两种数据操作工具,一种是MongoTemplate
他是mongodb数据访问模板提供了基础的数据访问接口。而另外一种是创建一种继承MongoRepository
的数据访问接口,MongoRepository
是基于JPA规则的数据访问接口,它提供了基础的CRUD操作,虽然无法实现一些复杂的统计以及联表查询,但是因为提供了自定义方法
使其对单表的操作上会非常简单。
基于MongoRepository的CRUD
保存&更新
MongoRepository的保存和更新都是使用save方法,而对于批量处理MongoRepository提供了saveAll方法
@Override
public void saveOne(Product product) {
productRepository.save(product);
}
@Override
public void batchSave(List<Product> products) {
productRepository.saveAll(products);
}
删除
MongoRepository
关于删除提供的是delete(实体对象)
的方法,此方法需要将mongodb实体作为参数传递进来,很多时候我们的删除都是基于ID
的,所以deleteById
使用的频率会比较多一些。但是基于ID的删除MongoRepository
并没有提供批量处理的方法,但是可以使用自定义方法
创建一个deleteAllByIdIn方法来实现批量删除
@Override
public void deleteOne(String id) {
productRepository.deleteById(id);
}
@Override
public void batchDelete(List<String> idList) {
productRepository.deleteAllByIdIn(idList);
}
查询
MongoRepository
关于查询需要配合Example使用,此类会将mongodb实体中的内容转换为查询的条件来进行筛选,而对于需要进行分页或排序操作的查询,需要配合Pageable和Sort的使用来实现数据分页。
@Override
public List<Product> queryByEntity(Product product) {
Example<Product> of = Example.of(product);
List<Product> all = productRepository.findAll(of);
return all;
}
@Override
public Page<Product> pageByEntity(Product product, int page, int size) {
Example<Product> of = Example.of(product);
Pageable pageable = PageRequest.of(page - 1, size);
Page<Product> all = productRepository.findAll(of, pageable);
return all;
}
需要注意,有些时候我们创建自定义方法
后需要进行分页查询,此时关于分页的内容并不需要在方法名称上做任何改动,只需要在最后添加入参Pageable,并将返回内容修改为Page<{mongodb数据实体}>此方法就可以使用分页参数并返回分页数据了。
基于MongoTemplate的CRUD
保存
MongoTemplate虽然很多查询条件需要用户自行设置,但是其也提供了基础的查询接口。比如MongoTemplate提供了save方法来保存数据。
@Override
public void saveOneByTemplate(Product product) {
mongoTemplate.save(product);
}
更新
MongoTemplate针对数据更新提供了upsert
、updateFirst
、updateMulti
等方法上面几种方法都是基于doUpdate
的实现的。这里就需要使用者提供一个定位更新数据的Query
。
/**
* 执行插入。
* @param queryProduct
* @param product
*/
@Override
public void updateOneByTemplate(Product queryProduct,Product product) {
Query query = new Query();
Criteria criteria = Criteria.byExample(Example.of(queryProduct));
query.addCriteria(criteria);
Update update = new Update();
update.set("id",product.getId());
update.set("productName",product.getProductName());
update.set("productMoney",product.getProductMoney());
mongoTemplate.updateMulti(query, update,Product.class);
}
删除
MongoTemplate的提供了remove
方法来进行删除,当然基于删除的操作也需要提供提供一个定位更新数据的Query
。MongoTemplate也提供了一个findAllAndRemove
发现并删除的接口,当然我实际开发中此接口使用的并不算多。
@Override
public void deleteOneByTemplate(String id) {
Query query = new Query();
Criteria criteria = Criteria.where("id").is(id);
query.addCriteria(criteria);
mongoTemplate.remove(query,Product.class);
}
@Override
public void batchDeleteByTemplate(List<String> idList) {
Query query = new Query();
Criteria criteria = Criteria.where("id").in(idList);
query.addCriteria(criteria);
mongoTemplate.remove(query,Product.class);
}
查询
MongoTemplate的查询是使用findXXX
来实现的,核心的内容依旧是Query
。和使用MongoRepository不同,MongoTemplate没有提供一个简单的分页查询接口,需要根据先求总数然后获取分页数据。需要注意的是其分页参数需要在Query
中设置,主要是skip
(起始行数)以及limit
(显示行数)
@Override
public List<Product> queryByEntityByTemplate(Product product) {
Query query = new Query();
Criteria criteria = Criteria.byExample(Example.of(product));
query.addCriteria(criteria);
List<Product> products = mongoTemplate.find(query, Product.class);
return products;
}
@Override
public Page<Product> pageByEntityByTemplate(Product product, long startNum, int size) {
Query query = new Query();
Criteria criteria = Criteria.byExample(Example.of(product));
query.addCriteria(criteria);
long count = mongoTemplate.count(query, Product.class);
// 分页参数
query.skip(startNum).limit(size);
List<Product> products = mongoTemplate.find(query, Product.class);
Page page = new PageImpl(products, Pageable.unpaged(),count);
return page;
}
个人水平有限,上面的内容可能存在没有描述清楚或者错误的地方,假如开发同学发现了,请及时告知,我会第一时间修改相关内容,也希望大家看在这个新春佳节只能宅到家中埋头苦逼的码代码的情况下,能给我点一个赞。你的点赞就是我前进的动力。在这里也祝大家新春快乐。