1. 添加依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- 可选:用于 JSON 序列化/反序列化 -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-legacy</artifactId>
<version>4.9.1</version>
</dependency>
2.自定义MongoDB配置
java
@Configuration
public class MongoConfig {
/**
* 自定义 MongoTemplate
*/
@Bean
public MongoTemplate mongoTemplate(MongoDatabaseFactory factory, MappingMongoConverter converter) {
return new MongoTemplate(factory, converter);
}
//配置 mongodb 事务管理器,能够让 mongodb 支持事务操作
@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
/**
* 自定义 MappingMongoConverter
*/
@Bean
public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory factory, MongoMappingContext context, MongoCustomConversions conversions) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, context);
// 自定义配置
converter.setTypeMapper(new DefaultMongoTypeMapper(null)); // 移除 _class 字段
converter.setCustomConversions(conversions); // 注册自定义转换器
converter.afterPropertiesSet();
return converter;
}
/**
* 注册自定义类型转换器
*/
@Bean
public MongoCustomConversions customConversions() {
return new MongoCustomConversions(Arrays.asList(
// 添加自定义转换器
new DateToZonedDateTimeConverter(),
new ZonedDateTimeToDateConverter()
));
}
}
// Date 转 ZonedDateTime
class DateToZonedDateTimeConverter implements Converter<Date, ZonedDateTime> {
@Override
public ZonedDateTime convert(Date source) {
return source.toInstant().atZone(ZoneId.systemDefault());
}
}
// ZonedDateTime 转 Date
class ZonedDateTimeToDateConverter implements Converter<ZonedDateTime, Date> {
@Override
public Date convert(ZonedDateTime source) {
return Date.from(source.toInstant());
}
}
3. 环境变量配置
3.1 单机版
(1)第一种方式:
TypeScript
server:
port: 8088
spring:
data:
mongodb:
# 连接字符串格式
# mongodb://用户名:密码@Ip地址:端口/数据库名
# 如果使用的是 root 角色的用户登录,则必须在后面加上 authSource=admin 参数
# 之前在 admin 库中创建了一个 root 角色的账号 jobs
# 在实际项目中,强烈建议,针对每个数据库创建一个 readwrite 角色的用户
uri: mongodb://root:root123456@192.168.146.132:27017/mytest?authSource=admin
# 允许在实体类上,通过 @Indexed 创建单字段索引,通过 @CompoundIndex 创建多字段联合索引
# 注意:这里只是演示注解的使用,实际项目中一般不推荐使用注解创建索引,
# 最好通过 mongodb 的命令操作 mongodb 管理索引
auto-index-creation: true
(2)第二种方式
python
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.port=27017
spring.data.mongodb.database=database_test
spring.data.mongodb.username=root123
spring.data.mongodb.password=root
3.2 集群模式
javascript
server:
port: 8088
spring:
data:
mongodb:
# 连接字符串格式
# mongodb://用户名:密码@Ip地址:端口/数据库名
# 如果使用的是 root 角色的用户登录,则必须在后面加上 authSource=admin 参数
# 之前在 admin 库中创建了一个 root 角色的账号 jobs
# 在实际项目中,强烈建议,针对每个数据库创建一个 readwrite 角色的用户
uri: mongodb://root:root123456@192.168.146.132:27017,192.168.146.132:27018,192.168.146.132:27019/mytest?authSource=admin&replicaSet=mongos&slaveOk=true
# 允许在实体类上,通过 @Indexed 创建单字段索引,通过 @CompoundIndex 创建多字段联合索引
# 注意:这里只是演示注解的使用,实际项目中一般不推荐使用注解创建索引,
# 最好通过 mongodb 的命令操作 mongodb 管理索引
auto-index-creation: true
注意:在连接 MongoDB(或类似服务)时,你的连接字符串(connection string)中的用户名或密码包含了特殊字符(如冒号: 或 at 符号 @) 。根据 URI 规范,这些特殊字符必须进行 URL 编码(urlencode),否则连接会失败。
- 找出你用到的连接字符串
一般格式如下:
mongodb://username:password@host:port/db
如果 username
或 password
里有 :
或 @
,必须 URL 编码。
- 如何 URL 编码
字符 | URL编码后形式 |
---|---|
: | %3A |
@ | %40 |
/ | %2F |
? | %3F |
= | %3D |
& | %26 |
# | %23 |
例子:
假设你的用户名是 user:na@me
,密码是 pass@word:123
,正常这样写会报错:
mongodb://user:na@me:pass@word:123@localhost:27017/test
应改为(正确写法):
用户名 user:na@me
变成 user%3Ana%40me
,密码 pass@word:123
变成 pass%40word%3A123
:
mongodb://user%3Ana%40me:pass%40word%3A123@localhost:27017/test
4.增删改查
4.1 创建实例类
java
//注意:
//这里只是演示相关注解的使用,但是不建议在实体类上通过注解去建立索引
//最好通过命令,直接操作 mongodb 的文档去建立相关索引
@Data
//使用该注解,标明要操作的 mongodb 的文档(相当于数据库的表)
@Document("tb_employee")
//建立多字段联合索引( 1 表示升序,-1 表示降序)
@CompoundIndex(def = "{'depart':1,'age':-1}")
//使用该注解,可以使用对象实例化赋值采用链式编写
@Accessors(chain = true)
public class Employee {
//使用该注解,标明 mongodb 文档的主键 id
@MongoId
private String id;
//使用该注解,针对单个字段创建索引,unique = true 表示唯一索引
@Indexed(unique = true)
private String workNo; //员工编号
//如果 mongodb 文档的字段名与该实体类的字段名不一致
//使用该注解,标明 mongodb 文档中实际的字段名
@Field("ename")
private String name; //姓名
private String depart; //部门
private Integer age; //年龄
private Integer money; //薪水
}
4.2 新增
java
@SpringBootTest
public class MongoTest {
//注入 spring-data-mongodb 自带的 MongoTemplate 对象
@Autowired
private MongoTemplate mongoTemplate;
//添加一批员工
@Test
public void testAdd() {
//其实不需要给 id 赋值,因为 mongodb 会自动生成主键 id
//insert 和 save 都是添加记录,它们的区别是:
//如果 id 在文档记录中已经存在的情况下,save 会更新,insert 会抛异常
//采用 insert 方法,主键不存在就添加,存在就抛异常
Employee emp1 = new Employee().setId("001")
.setWorkNo("nb001").setName("任肥肥").setDepart("研发部").setAge(38).setMoney(2500);
mongoTemplate.insert(emp1);
//采用 save 方法,主键存在就更新,不存在就新增
Employee emp4 = new Employee().setId("004")
.setWorkNo("nb004").setName("乔豆豆").setDepart("财务部").setAge(39).setMoney(1800);
mongoTemplate.save(emp4);
}
/**
* 批量插入
*/
@Test
void m4() {
List<Person> persons = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
Person person = Person.builder().name("张三"+i).age(i).id(Long.valueOf(i)).build();
persons.add(person);
}
mongoTemplate.insertAll(persons);
}
}
4.3 查找
java
@SpringBootTest
public class MongoTest {
//注入 spring-data-mongodb 自带的 MongoTemplate 对象
@Autowired
private MongoTemplate mongoTemplate;
//查询所有员工
@Test
public void testFindAll() {
List<Employee> list = mongoTemplate.findAll(Employee.class);
for (Employee emp : list) {
System.out.println(emp);
}
}
//条件查询:查询【研发部】,并且年龄大于 30 岁的员工,按照年龄【倒序】排列
@Test
public void testFindWhere() {
Criteria criteria = Criteria.where("depart").is("研发部").and("age").gt(30);
Query query = new Query(criteria).with(Sort.by(Sort.Order.desc("age")));
List<Employee> list = mongoTemplate.find(query, Employee.class);
for (Employee emp : list) {
System.out.println(emp);
}
}
//将年龄在 30 岁以下的员工,薪水增加 100 元,部门改为大数据部门
@Test
public void testUpdate() {
Query query = Query.query(Criteria.where("age").gt(30));
Update update = new Update();
update.inc("money", 100);
update.set("depart", "新部门");
UpdateResult updateResult = mongoTemplate.updateMulti(query, update, Employee.class);
//打印修改的文档数量
System.out.println(updateResult.getModifiedCount());
}
//分页查询(查询薪水大于等于 2000 的员工,按照薪水【升序】排列)
@Test
public void testPage() {
int page = 2;
int size = 2;
Criteria criteria = Criteria.where("money").gt(2000);
Query queryCount = new Query(criteria);
long total = mongoTemplate.count(queryCount, Employee.class);
System.out.println("一共有 " + total + " 条记录,其中第 " + page + " 页的结果为:");
Query queryLimit = new Query(criteria)
.skip((page - 1) * size) //跳过多少条
.limit(size) //返回多少条
.with(Sort.by(Sort.Order.asc("money")));
List<Employee> list = mongoTemplate.find(queryLimit, Employee.class);
for (Employee emp : list) {
System.out.println(emp);
}
}
//统计每个部门的人数
@Test
public void testGroupCout() {
// 统计各个部门的人数
AggregationOperation group = Aggregation.group("depart").count().as("empCount");
// 将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(group);
// 执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, Employee.class, Map.class);
for (Map result : results.getMappedResults()) {
System.out.println(result);
}
}
//统计每个部门,最高的薪水是多少
@Test
public void testGroupMax() {
//这里使用的是 max ,当然你也可以使用 min(最小),sum(总和),avg(平均)等方法
AggregationOperation group = Aggregation.group("depart").max("money").as("maxMoney");
Aggregation aggregation = Aggregation.newAggregation(group);
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, Employee.class, Map.class);
for (Map result : results.getMappedResults()) {
System.out.println(result);
}
}
//随机获取 3 个人的信息
@Test
public void testGetRandomData() {
//创建统计对象,设置统计参数,sample 表示随机获取数据
TypedAggregation aggregation = Aggregation.newAggregation(Employee.class, Aggregation.sample(3));
//调用mongoTemplate方法统计
AggregationResults<Employee> results = mongoTemplate.aggregate(aggregation, Employee.class);
//获取统计结果
List<Employee> list = results.getMappedResults();
//循环打印出来
list.forEach(System.out::println);
}
//随机获取 2 个年龄大于 26 岁的员工
@Test
public void testGetWhereRandomData() {
Criteria criteria = Criteria.where("age").gt(26);
TypedAggregation<Employee> aggregation =
TypedAggregation.newAggregation(Employee.class,
Aggregation.match(criteria), Aggregation.sample(2));
AggregationResults<Employee> results = mongoTemplate.aggregate(aggregation, Employee.class);
List<Employee> list = results.getMappedResults();
list.forEach(System.out::println);
}
}
java
@SpringBootTest
class QueryTests {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 查询集合中的全部文档数据
*/
@Test
public void findAll() {
List<Person> personList = mongoTemplate.findAll(Person.class);
System.out.println("查询结果:" + personList.toString());
}
/**
* 查询集合中指定的ID文档数据
*/
@Test
public void findById() {
Person byId = mongoTemplate.findById(3L, Person.class);
System.out.println("查询结果:" + byId.toString());
}
/**
* 根据条件查询符合条件的文档数据并返回第一条数据
*/
@Test
public void findOne() {
Query query = new Query(Criteria.where("name").is("宋江1"));
Person result = mongoTemplate.findOne(query, Person.class);
System.out.println("查询结果:" + result.toString());
}
/**
* 根据条件查询所有符合条件的文档
*/
@Test
public void findByCondition() {
Query query = new Query(Criteria.where("age").gt(18));
List<Person> result = mongoTemplate.find(query, Person.class);
System.out.println("查询结果:" + result.toString());
}
/**
* 根据【AND】关联多个查询条件,查询集合中所有符合条件的文档数据
*/
@Test
public void findByAndCondition() {
// 创建条件
Criteria name = Criteria.where("name").is("张三");
Criteria age = Criteria.where("age").is(18);
// 创建条件对象,将上面条件进行 AND 关联
Criteria criteria = new Criteria().andOperator(name, age);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
List<Person> result = mongoTemplate.find(query, Person.class);
System.out.println("查询结果:" + result.toString());
}
/**
* 根据【OR】关联多个查询条件,查询集合中的文档数据
*/
@Test
public void findByOrCondition() {
// 创建条件
Criteria criteriaUserName = Criteria.where("name").is("张三");
Criteria criteriaPassWord = Criteria.where("age").is(22);
// 创建条件对象,将上面条件进行 OR 关联
Criteria criteria = new Criteria().orOperator(criteriaUserName, criteriaPassWord);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
List<Person> result = mongoTemplate.find(query, Person.class);
System.out.println("查询结果:" + result.toString());
}
/**
* 根据【IN】关联多个查询条件,查询集合中的文档数据
*/
@Test
public void findByInCondition() {
// 设置查询条件参数
List<Long> ids = Arrays.asList(10L, 11L, 12L);
// 创建条件
Criteria criteria = Criteria.where("id").in(ids);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
List<Person> result = mongoTemplate.find(query, Person.class);
System.out.println("查询结果:" + result.toString());
}
/**
* 根据【逻辑运算符】查询集合中的文档数据
*/
@Test
public void findByOperator() {
// 设置查询条件参数
int min = 20;
int max = 35;
Criteria criteria = Criteria.where("age").gt(min).lte(max);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
List<Person> result = mongoTemplate.find(query, Person.class);
System.out.println("查询结果:" + result.toString());
}
/**
* 根据【正则表达式】查询集合中的文档数据
*/
@Test
public void findByRegex() {
// 设置查询条件参数
String regex = "^张";
Criteria criteria = Criteria.where("name").regex(regex);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
List<Person> result = mongoTemplate.find(query, Person.class);
System.out.println("查询结果:" + result.toString());
}
/**
* 根据条件查询集合中符合条件的文档,获取其文档列表并排序
*/
@Test
public void findByConditionAndSort() {
Query query = new Query(Criteria.where("name").is("张三")).with(Sort.by("age"));
List<Person> result = mongoTemplate.find(query, Person.class);
System.out.println("查询结果:" + result.toString());
}
/**
* 根据单个条件查询集合中的文档数据,并按指定字段进行排序与限制指定数目
*/
@Test
public void findByConditionAndSortLimit() {
String userName = "张三";
//从第5行开始,查询3条数据返回
Query query = new Query(Criteria.where("name").is("张三"))
.with(Sort.by("createTime"))
.limit(3).skip(5);
List<Person> result = mongoTemplate.find(query, Person.class);
System.out.println("查询结果:" + result.toString());
}
/**
* 统计集合中符合【查询条件】的文档【数量】
*/
@Test
public void countNumber() {
// 设置查询条件参数
String regex = "^张*";
Criteria criteria = Criteria.where("name").regex(regex);
// 创建查询对象,然后将条件对象添加到其中
Query query = new Query(criteria);
long count = mongoTemplate.count(query, Person.class);
System.out.println("统计结果:" + count);
}
}
4.4 删除
java
@SpringBootTest
class DeleteTests {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 删除id 为1的记录
*/
@Test
void m1() {
Query query = new Query(Criteria.where("id").is(1L));
DeleteResult remove = mongoTemplate.remove(query, Person.class);
System.out.println("删除的条数为:" + remove.getDeletedCount());//1
}
/**
* 删除符合条件的所有文档
*/
@Test
public void m2() throws Exception {
//删除年龄小于18的所有人: lt 表示小于等于
Query query = new Query(Criteria.where("age").lt(18));
DeleteResult result = mongoTemplate.remove(query, Person.class);
System.out.println("删除条数:" + result.getDeletedCount());
}
/**
* 删除符合条件的单个文档 并返回删除的文档
*/
@Test
public void m3() throws Exception {
Query query = new Query(Criteria.where("id").is(2L));
Person per = mongoTemplate.findAndRemove(query, Person.class);
System.out.println(per);//Person(id=2, name=宋江, age=2)
}
/**
* 删除符合条件的所有文档
* 并返回删除的所有文档
*/
@Test
public void m4(){
// lt 表示小于
Query query = new Query(Criteria.where("age").lt(3));
List<Person> pers = mongoTemplate.findAllAndRemove(query, Person.class);
System.out.println(pers);
}
}