MongoDB基本使用(包含工具类)

示例代码

zr/MongoDemo

✅ 1. MongoDB 概述

🔷1.1 MongoDB 是什么?

  • 文档数据库(Document DB)
  • 数据以 BSON 格式存储(类似 JSON)
  • 每条数据叫 document
  • 多条 document 存储在 collection
  • 多个 collection 组成 database

🔷1.2 与 MySQL 的对比

MySQLMongoDB
关系型表结构无固定 schema
强类型弱结构,字段可变
JOIN 多无 JOIN,需要聚合
行式存储文档存储(嵌套结构)
适合强一致适合灵活可扩展场景

🔷1.3 文档示例

{
  "_id": "123",
  "name": "eric",
  "tags": ["dev", "java"],
  "address": {
    "city": "shanghai",
    "code": 200000
  }
}

✅ 2. Docker 启动 MongoDB

🔷2.1 拉取镜像

docker pull mongo:7.0

🔷2.2 启动单节点 MongoDB(开发环境)

docker run -d \
  --name mongodb \
  -p 27017:27017 \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=123456 \
  mongo:7.0

🔷2.3 使用图形化界面(强烈推荐)

✅ 方案 1:直接下载安装(最推荐,官方方式)

官网下载:
https://www.mongodb.com/products/tools/compass

✅ 方案 2:当然,用navicat连接也是可以(个人推荐)


✅ 3. Spring Boot 使用 MongoDB

🔷3.1 添加依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.59</version>
</dependency>

🔷3.2 配置 application.yml

spring:
  data:
    mongodb:
      uri: mongodb://admin:123456@localhost:27017/test?authSource=admin

🔷3.3 创建实体

@Data
@Document("user")
public class User {
    @Id
    private String id;
    private String name;
    private int age;
}

🔥3.4 对mongo进行操作

🔹3.4.1 对比

MongoRepository = 简单 CRUD 自动化,像 JPA
MongoTemplate = 灵活强大、复杂查询必用,像 MyBatis

特性MongoRepositoryMongoTemplate
编程风格声明式(接口)命令式(代码)
上手难度极低较高
CRUD 能力简单 CRUD 自动生成CRUD 全手写
复杂查询能力弱(QueryDSL 才稍强)能力最强(支持聚合、管道等)
聚合操作(Aggregation)不支持✅ 支持
多条件动态查询不方便✅ 最适合
分页排序简单支持✅ 全支持
事务控制支持支持
适合场景普通 CRUD、快速开发高级查询、复杂条件、聚合分析
🔹3.4.1 Repository 使用方式(最简单)
  1. 继承MongoRepository
  public interface UserRepository extends MongoRepository<User, String> {
      List<User> findByName(String name);
  }
  1. 编写UserService
package com.zr.mongodemo.service;

import com.zr.mongodemo.entity.User;
import com.zr.mongodemo.repository.UserRepository;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author: zr
 * @Date: 2025/11/03/13:59
 * @Description:
 */
@Service
public class UserService {
    private final UserRepository repo;

    public UserService(UserRepository repo) {
        this.repo = repo;
    }

    public User create(User user) {
        return repo.save(user);
    }

    public User get(String id) {
        return repo.findById(id).orElse(null);
    }

    public List<User> list() {
        return repo.findAll();
    }

    public User update(User user) {
        return repo.save(user);   // save = insert or update
    }

    public void delete(String id) {
        repo.deleteById(id);
    }
}
  1. 编写测试类
package com.zr.mongodemo;

import com.alibaba.fastjson2.JSON;
import com.zr.mongodemo.entity.User;
import com.zr.mongodemo.repository.UserRepository;
import com.zr.mongodemo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
@Slf4j
class MongoDemoApplicationTests {

    @Autowired
    private UserService userService;
    @Test
    void create() {
        User user = new User();
        user.setId("2");
        user.setName("zr2");
        user.setAge(19);
        userService.create(user);
    }

    @Test
    void list() {
        List<User> list = userService.list();
        log.info("list: {}", JSON.toJSONString(list));
    }
    @Test
    void get() {
        User user = userService.get("1");
        log.info("user: {}", JSON.toJSONString(user));
    }

    @Test
    void update() {
        User user = new User();
        user.setId("1");
        user.setAge(20);
        userService.update(user);
    }

    @Test
    void delete() {
        userService.delete("1");
    }

}

3.1 测试新增

可以看到新增了一条数据

3.2 测试列表查询

可以看到返回了多条数据

3.3 测试id获取

可以看到返回了对应id的数据

3.4 测试更新

此处发现一个问题,没有设置的字段会被清空

3.4.1 只更新不为空的字段

回滚数据

创建MongoUpdateUtil使用反射的方式,只对每个非空字段做赋值处理

package com.zr.mongodemo.util;

import org.springframework.data.mongodb.core.query.Update;

import java.lang.reflect.Field;

/**
 * @Author: zr
 * @Date: 2025/11/03/14:44
 * @Description:
 */
public class MongoUpdateUtil {
    public static Update convertToUpdate(Object obj) {
        Update update = new Update();
        Field[] fields = obj.getClass().getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);

            try {
                Object value = field.get(obj);

                // 跳过 id,跳过 null
                if (value != null && !"id".equals(field.getName())) {
                    update.set(field.getName(), value);
                }

            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return update;
    }
}

此处还是用到了mongoTemplate

    public void update2(User user) {
        Query query = Query.query(Criteria.where("_id").is(user.getId()));

        Update update = MongoUpdateUtil.convertToUpdate(user);

        mongoTemplate.updateFirst(query, update, User.class);
    }

修改id为1的年龄为20

可以看到没有更新的字段没有被清空,需要更新的字段也更新了

3.5 删除

可以看到对应数据被删除了

🔹3.4.2 Template 使用方式(更灵活)
@Autowired
MongoTemplate mongoTemplate;

public User save(User user) {
return mongoTemplate.save(user);
}

工具类对mongo进行操作

package com.zr.mongodemo.util;

import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
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.lang.reflect.Field;
import java.util.List;
import java.util.Map;


/**
 * @Author: zr
 * @Date: 2025/11/03/15:06
 * @Description:
 */
public class MongoTemplateUtil {
    private final MongoTemplate mongoTemplate;

    public MongoTemplateUtil(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    // ✅ 插入
    public <T> T insert(T obj) {
        return mongoTemplate.insert(obj);
    }

    // ✅ 根据 ID 查询
    public <T> T findById(String id, Class<T> clazz) {
        return mongoTemplate.findById(id, clazz);
    }

    // ✅ 查询所有
    public <T> List<T> findAll(Class<T> clazz) {
        return mongoTemplate.findAll(clazz);
    }

    // ✅ 单字段查询
    public <T> List<T> findByField(String field, Object value, Class<T> clazz) {
        Query query = Query.query(Criteria.where(field).is(value));
        return mongoTemplate.find(query, clazz);
    }

    // ✅ 多条件精确查询
    public <T> List<T> findByFields(Map<String, Object> map, Class<T> clazz) {
        Query query = new Query();
        map.forEach((k, v) -> query.addCriteria(Criteria.where(k).is(v)));
        return mongoTemplate.find(query, clazz);
    }

    // ✅ 分页查询 + 排序
    public <T> List<T> findPage(Query query, int page, int size, String sortField, boolean asc, Class<T> clazz) {
        if (sortField != null) {
            query.with(Sort.by(asc ? Sort.Direction.ASC : Sort.Direction.DESC, sortField));
        }
        query.skip((long) (page - 1) * size);
        query.limit(size);
        return mongoTemplate.find(query, clazz);
    }

    // ✅ 获取数量
    public long count(Query query, Class<?> clazz) {
        return mongoTemplate.count(query, clazz);
    }

    // ✅ 删除
    public void deleteById(String id, Class<?> clazz) {
        Query query = Query.query(Criteria.where("_id").is(id));
        mongoTemplate.remove(query, clazz);
    }

    // ✅ 按条件批量删除
    public void deleteByFields(Map<String, Object> map, Class<?> clazz) {
        Query query = new Query();
        map.forEach((k, v) -> query.addCriteria(new Criteria(k).is(v)));
        mongoTemplate.remove(query, clazz);
    }

    // ✅ ✅ 动态局部更新(自动忽略 null 字段)
    public <T> void updatePartialById(T obj) {
        try {
            Field idField = obj.getClass().getDeclaredField("id");
            idField.setAccessible(true);
            Object idValue = idField.get(obj);

            if (idValue == null) {
                throw new RuntimeException("id 不能为空");
            }

            Query query = Query.query(Criteria.where("_id").is(idValue));
            Update update = buildDynamicUpdate(obj);

            mongoTemplate.updateFirst(query, update, obj.getClass());

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // ✅ 核心:构建动态 Update,只更新非 null 字段
    private <T> Update buildDynamicUpdate(T obj) {
        Update update = new Update();
        Field[] fields = obj.getClass().getDeclaredFields();

        for (Field f : fields) {
            f.setAccessible(true);
            try {
                Object value = f.get(obj);
                String fieldName = f.getName();

                if ("id".equals(fieldName) || value == null) {
                    continue;
                }
                update.set(fieldName, value);

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return update;
    }

    // ✅ 条件更新(多条件 WHERE)
    public <T> void updatePartialByQuery(Query query, T obj, Class<T> clazz) {
        Update update = buildDynamicUpdate(obj);
        mongoTemplate.updateMulti(query, update, clazz);
    }
}

该工具类支持的能力

功能方法
新增insert(obj)
根据 ID 查询findById(id)
全查询findAll()
单字段查询findByField()
多字段查询findByFields()
分页findPage()
获取数量count()
删除 IDdeleteById()
条件批量删除deleteByFields()
✅ 动态局部更新(自动忽略 null)updatePartialById()
✅ 条件更新updatePartialByQuery()

使用示例

package com.zr.mongodemo.service;

import com.zr.mongodemo.entity.User;
import com.zr.mongodemo.repository.UserRepository;
import com.zr.mongodemo.util.MongoTemplateUtil;
import com.zr.mongodemo.util.MongoUpdateUtil;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
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 org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author: zr
 * @Date: 2025/11/03/13:59
 * @Description:
 */
@Service
public class UserServiceTemplate {
    private final MongoTemplateUtil mongoUtils;

    @Autowired
    public UserServiceTemplate(MongoTemplate mongoTemplate) {
        this.mongoUtils = new MongoTemplateUtil(mongoTemplate);
    }

    public void add(User user) {
        mongoUtils.insert(user);
    }

    public User get(String id) {
        return mongoUtils.findById(id, User.class);
    }

    public void update(User user) {
        mongoUtils.updatePartialById(user); // ✅ 动态局部更新
    }

    public void delete(String id) {
        mongoUtils.deleteById(id, User.class);
    }

    public List<User> list() {
        return mongoUtils.findAll(User.class);
    }
}

✅ 4. 高级用法(非常实用)

🔶4.1 建索引

@Document("user")
public class User {
    @Indexed
    private String name;
}

🔶4.2 聚合查询(类似 SQL group by)

Aggregation agg = Aggregation.newAggregation(
    Aggregation.group("age").count().as("count")
);

List<Document> result = mongoTemplate.aggregate(agg, "user", Document.class).getMappedResults();

🔶4.3 分页查询

Query query = new Query().limit(10).skip(20);
List<User> list = mongoTemplate.find(query, User.class);

✅ 5. Docker Compose 部署 MongoDB 副本集(生产环境)

当你需要:
✔ 数据高可用
✔ 支持事务
✔ 副本集复制

使用下面配置:

🔶docker-compose.yml

version: "3"
services:
  mongo1:
    image: mongo:7.0
    container_name: mongo1
    ports:
      - "27017:27017"
    environment:
      - MONGO_INITDB_ROOT_USERNAME=admin
      - MONGO_INITDB_ROOT_PASSWORD=123456
      - TZ=Asia/Shanghai
    command: ["--replSet", "rs0"]

  mongo2:
    image: mongo:7.0
    container_name: mongo2
    environment:
      - MONGO_INITDB_ROOT_USERNAME=admin
      - MONGO_INITDB_ROOT_PASSWORD=123456
    command: ["--replSet", "rs0"]

  mongo3:
    image: mongo:7.0
    container_name: mongo3
    environment:
      - MONGO_INITDB_ROOT_USERNAME=admin
      - MONGO_INITDB_ROOT_PASSWORD=123456
    command: ["--replSet", "rs0"]

✅ 6. 常见错误与解决方案

❗ Invalid BSON field name

出现:

Invalid BSON field name

原因:
✅ MongoDB key 不能包含以下字符:

  • "."
  • "$"

比如错误例子:

{
  "status.pass": "ok"  // 错误
}

解决方式:

  • 替换 “.” → “_”
  • 使用 @Field 映射
  • 复杂结构用 Document 包装

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值