使用 SpringData 操作 Mongodb
本篇博客主要介绍 SpringBoot 如何通过 SpringData 操作 Mongodb。在上篇博客部署的 mongodb 为了方便,在 admin 库中创建了一个 root 角色的账号,使用这个账号具有最高权限,可以访问和操作任何库。在实际项目中强烈建议为每个 mongodb 数据库创建一个低权限角色的用户,比如具有 readwrite 角色的用户。
有关 mongodb 支持的数据类型,以及索引视图等等,这里不做介绍,熟悉关系型数据库的小伙伴,对此肯定很容易入门,具体细节可以参考官网。本篇博客以 Demo 代码的方式介绍如何操作 mongodb,在博客最后会提供源代码下载。
Mongodb 的中文官网地址:https://www.mongodb.com/zh-cn
一、工程搭建
我的虚拟机 ip 地址是:192.168.136.128,参照上篇博客部署 mongodb,使用 docker-compose 部署并初始化一个 root 角色的账号是 jobs ,密码是 123456,本篇博客的 Demo 代码,连接这个 mongodb 进行操作演示。
搭建一个 SpringBoot 工程,结构如下所示:
MongoTransactionConfig 是对 mongodb 进行事务配置,使其支持事务操作。
TransactionService 中编写了一个方法,用来测试 mongodb 对事务的支持。
Employee 是员工实体类,针对 mongodb 数据库中要操作的表 tb_employee 而创建。
MongoTest 类中编写了一些测试方法,用来对 mongodb 中 tb_employee 表进行增删改查。
该工程的 pom 文件内容如下:(最主要的是引入 spring-boot-starter-data-mongodb 这个依赖)
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jobs</groupId> <artifactId>springboot_mongo</artifactId> <version>1.0</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.9.RELEASE</version> </parent> <dependencies> <!--引入最基本的 springboot 依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!--引入 springdata mongodb 的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> <!--引入 test 依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--引入该依赖,可以打印日志,以及省去实例类的 get 和 set 方法--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
二、代码细节
对于 mongodb 来说,不需要提前创建表,当添加数据后自动就会创建表。
本 demo 中以操作员工为例,创建一个 Employee 类,具体细节如下:
package com.jobs.pojo; import lombok.Data; import lombok.experimental.Accessors; import org.springframework.data.mongodb.core.index.CompoundIndex; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoId; //注意: //这里只是演示相关注解的使用,但是不建议在实体类上通过注解去建立索引 //最好通过命令,直接操作 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; //薪水 }
需要注意的是:上面虽然引用了 @Indexed 和 @CompoundIndex 去自动为 mongodb 中的 tb_employee 创建索引,但是默认情况下并不会生效,必须在 yml 配置文件中将 auto-index-creation 配置为 true 才会生效。当然这里只是演示注解的使用,在实际项目中不建议通过注解去创建索引,建议根据实际需要,通过 mongodb 的命令去操作 mongodb 创建索引。
项目的 application.yml 配置文件内容如下所示:(主要是配置 mongodb 的连接字符串信息)
spring: data: mongodb: # 连接字符串格式 # mongodb://用户名:密码@Ip地址:端口/数据库名 # 如果使用的是 root 角色的用户登录,则必须在后面加上 authSource=admin 参数 # 之前在 admin 库中创建了一个 root 角色的账号 jobs # 在实际项目中,强烈建议,针对每个数据库创建一个 readwrite 角色的用户 uri: mongodb://jobs:123456@192.168.136.128:27017/mytest?authSource=admin # 允许在实体类上,通过 @Indexed 创建单字段索引,通过 @CompoundIndex 创建多字段联合索引 # 注意:这里只是演示注解的使用,实际项目中一般不推荐使用注解创建索引, # 最好通过 mongodb 的命令操作 mongodb 管理索引 auto-index-creation: true
接下来我们就可以编写测试类,通过 Employee 类去对 mongodb 中的 tb_employee 表进行增删改查了
package com.jobs.test; import com.jobs.pojo.Employee; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationOperation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import java.util.List; import java.util.Map; @SpringBootTest public class MongoTest { //注入 spring-data-mongodb 自带的 MongoTemplate 对象 @Autowired private MongoTemplate mongoTemplate; //添加一批员工 @Test public void testAdd() { //其实不需要给 id 赋值,因为 mongodb 会自动生成主键 id //insert 和 save 都是添加记录,它们的区别是: //如果 id 在文档记录中已经存在的情况下,save 会更新,insert 会抛异常 //前 3 条记录,采用 insert 方法,主键不存在就添加,存在就抛异常 Employee emp1 = new Employee().setId("001") .setWorkNo("nb001").setName("任肥肥").setDepart("研发部").setAge(38).setMoney(2500); mongoTemplate.insert(emp1); Employee emp2 = new Employee().setId("002") .setWorkNo("nb002").setName("候胖胖").setDepart("研发部").setAge(40).setMoney(2900); mongoTemplate.insert(emp2); Employee emp3 = new Employee().setId("003") .setWorkNo("nb003").setName("蔺赞赞").setDepart("商务部").setAge(36).setMoney(2700); mongoTemplate.insert(emp3); //后面这些记录,采用 save 方法,主键存在就更新,不存在就新增 Employee emp4 = new Employee().setId("004") .setWorkNo("nb004").setName("乔豆豆").setDepart("财务部").setAge(39).setMoney(1800); mongoTemplate.save(emp4); Employee emp5 = new Employee().setId("005") .setWorkNo("nb005").setName("李吨吨").setDepart("研发部").setAge(25).setMoney(2300); mongoTemplate.save(emp5); Employee emp6 = new Employee().setId("006") .setWorkNo("nb006").setName("杨壮壮").setDepart("研发部").setAge(28).setMoney(2200); mongoTemplate.save(emp6); } //查询所有员工 @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").lt(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); } //删除【薪水最高】的老油条员工 @Test public void testDelete() { //先找到薪水最高的老油条员工 Query maxQuery = new Query().with(Sort.by(Sort.Order.desc("money"))); Employee one = mongoTemplate.findOne(maxQuery, Employee.class); //打印出老油条员工 System.out.println(one); if (one != null) { //这里直接删除了,当然你也可以再通过查询条件,比如主键 id 进行删除 DeleteResult remove = mongoTemplate.remove(one); //打印出删除的文档数量 System.out.println(remove.getDeletedCount()); } } }
以上代码展示了对 mongodb 的增删改查,其中包含聚合查询,注释比较详细,应该很容易看懂。
通过 navicat 可以查看到 mongodb 中具体的数据,下图是操作过程中的截图:
三、事务支持
为了能够让 mongodb 支持事务,我们需要增加对事务的配置,代码如下:
package com.jobs.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.MongoTransactionManager; @Configuration public class MongoTransactionConfig { //配置 mongodb 事务管理器,能够让 mongodb 支持事务操作 @Bean MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) { return new MongoTransactionManager(dbFactory); } }
然后在 Service 中编写一个方法,用于测试对事务的支持
package com.jobs.service; import com.jobs.pojo.Employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class TransactionService { //注入 spring-data-mongodb 自带的 MongoTemplate 对象 @Autowired private MongoTemplate mongoTemplate; @Transactional public void transactionTest() { Employee emp = new Employee().setId("100") .setWorkNo("nb101").setName("任我行").setDepart("总裁办").setAge(44).setMoney(9999); mongoTemplate.save(emp); //抛出异常,导致添加的数据,回滚撤回 int num = 1 / 0; } }
然后在 MongoTest 测试方法中,编写一个测试方法,去调用 Service 方法,测试对事务的支持
package com.jobs.test; import com.jobs.service.TransactionService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class MongoTest { @Autowired private TransactionService transactionService; //测试 mongodb 的事务 @Test public void testTransaction() { //该方法中,去掉抛出异常的代码,则可以正常添加。 //如果方法中抛出异常,则会进行事务回滚 //从而证明:mongodb 也支持事务操作 transactionService.transactionTest(); } }
OK,以上就是 SpringData 操作 Mongodb 的介绍,所有代码都经过测试没有问题。
本篇博客的源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/springboot_mongo.zip
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
2021-08-15 Markdown文档编辑实用语法