SpringBoot 整合 自定义MongoDB

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),否则连接会失败。

  1. 找出你用到的连接字符串

一般格式如下:

mongodb://username:password@host:port/db

如果 usernamepassword 里有 :@必须 URL 编码

  1. 如何 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);
    }
}
相关推荐
许苑向上34 分钟前
Spring Boot 自动装配底层源码实现详解
java·spring boot·后端
星月昭铭2 小时前
Spring AI调用Embedding模型返回HTTP 400:Invalid HTTP request received分析处理
人工智能·spring boot·python·spring·ai·embedding
超级小忍3 小时前
深入浅出:在 Spring Boot 中构建实时应用 - 全面掌握 WebSocket
spring boot·后端·websocket
没有bug.的程序员3 小时前
《Spring Security源码深度剖析:Filter链与权限控制模型》
java·后端·spring·security·filter·权限控制
无责任此方_修行中4 小时前
不止是 AI 热潮:AWS 2025 技术峰会带给我的思考
后端·架构·aws
lang201509284 小时前
Apache Ignite 与 Spring Boot 集成
spring boot·后端·apache·ignite
Asthenia04124 小时前
深入剖析 Spring Boot 请求处理链路与 Servlet 的本质
后端
旧时光巷4 小时前
【Flask 基础 ①】 | 路由、参数与模板渲染
后端·python·零基础·flask·web·模板渲染·路由系统
小醉你真好4 小时前
Spring Boot 数据源配置中为什么可以不用写 driver-class-name
spring boot·后端·源代码管理
SirLancelot15 小时前
数据结构-Set集合(一)Set集合介绍、优缺点
java·开发语言·数据结构·后端·算法·哈希算法·set