MongoDB在Spring Boot中的使用方式举例介绍

 更新时间:2025年05月09日 11:12:51   作者:CY—CY  
MongoDB是基于Json数据结构的非关系型数据库,每一个Json文档有一个唯一的主键,下面通过实例代码讲解MongoDB在Spring Boot中的使用方式,感兴趣的朋友一起看看吧

引言:

常见的数据存储库有很多,例如常见的Mysql、Redis、PostgreSql。但在当前敏捷开发的时代,MongoDB不需要设计数据库的结构,省去了设计步骤。在扩展时,支持水平扩展,直接添加新的服务器存储节点,使得系统能够轻松适应大规模的数据和高并发的访问。同样支持索引结构。读了下面的文章,有直截了当的例子,以及实战案例,大家可以直接Ctrl C+V,移植到在自己的代码中。另外例子中富含注释,大家只要花一些时间读一读,都会理解并且快速上手使用。那么下面就由我给大家讲讲。

MongoDB的形式:

MongoDB是基于Json数据结构的非关系型数据库,每一个Json文档有一个唯一的主键。

一、在SpringBoot项目中整合MongoDB,首先需要引入Pom文件依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

二、在yaml配置文件中进行配置,Spring会自动扫描配置文件,并且利用。

spring:
  data:
    mongodb:
      uri: mongodb://username:password@ip:port/dataBaseName
      # username、password、ip、port、dataBaseName名称需要写自己的

三、使用上,我们定义一个Service类,里面封装好Mongo操作,在这里将这个Mongo Service开放给大家,方便大家使用:

//利用泛型传参,有效的使代码结构清晰,代码能够重复利用。另外,这里面的增、删、改、查操作都是在添加事务的情况下。
@Service
public class BaseService<T, ID> {
    private MongoTemplate mongoTemplate;
    private MongoClient client;
    @Resource
    public void setClient(MongoClient client) {
        this.client = client;
    }
    @Resource
    public void setMongoTemplate(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }
    /**
     * MongoDB事务进行数据一致性的管理。涉及失败回滚操作
     * @param entity 添加数据
     * @param <S>    数据类型
     * @return 添加的数据
     */
    public <S extends T> S insert(S entity) {
        ClientSession clientSession = client.startSession();
        try {
            //开始事务
            clientSession.startTransaction();
            return mongoTemplate.save(entity);
        } catch (Exception e) {
            //回滚
            clientSession.abortTransaction();
            throw new BadRequestException(e.getMessage());
        }finally {
            // 无论是否发生异常,都要结束会话。释放资源
            clientSession.close();
        }
    }
    /**
     * 主键删除
     *
     * @param s   删除数据
     * @param id  主键id
     * @param <S> 删除数据类型
     * @return 删除结果
     */
    public <S extends T> DeleteResult deleteById(S s, ID id) {
        ClientSession clientSession = client.startSession();
        try {
            clientSession.startTransaction();
            Query query = Query.query(Criteria.where("_id").is(id));
            return mongoTemplate.remove(query, s.getClass());
        } catch (Exception e) {
            clientSession.abortTransaction();
            throw new BadRequestException(e.getMessage());
        }
    }
    /**
     * 批量删除
     *
     * @param s   删除数据
     * @param ids 主键id
     * @param <S> 删除数据类型
     * @return 删除结果
     */
    public <S extends T> DeleteResult deleteByIds(S s, List<ID> ids) {
        ClientSession clientSession = client.startSession();
        try {
            clientSession.startTransaction();
            Query query = Query.query(Criteria.where("_id").in(ids));
            return mongoTemplate.remove(query, s.getClass());
        } catch (Exception e) {
            clientSession.abortTransaction();
            throw new BadRequestException(e.getMessage());
        }
    }
    /**
     * 修改
     *
     * @param s   修改数据
     * @param id  主键id
     * @param <S> 修改数据类型
     * @return 修改结果
     */
    public <S extends T> UpdateResult update(S s, String id) {
        ClientSession clientSession = client.startSession();
        try {
            clientSession.startTransaction();
            Query query = Query.query(Criteria.where("_id").is(id));
            Update update = new Update();
            Field[] fields = s.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                //获取属性
                String name = field.getName();
                //获取属性值
                Object value = field.get(s);
                if (ObjectUtils.allNotNull(value)) {
                    update.set(name, value);
                }
            }
            return mongoTemplate.updateFirst(query, update, s.getClass());
        } catch (Exception e) {
            clientSession.abortTransaction();
            throw new BadRequestException(e.getMessage());
        }
    }
    /**
     * 根据条件批量修改
     *
     * @param s      修改数据
     * @param query  修改条件
     * @param update 修改内容
     * @param <S>    修改数据类型
     * @return 修改结果
     */
    public <S extends T> UpdateResult updateByQuery(S s, Query query, Update update) {
        ClientSession clientSession = client.startSession();
        try {
            clientSession.startTransaction();
            return mongoTemplate.updateMulti(query, update, s.getClass());
        } catch (Exception e) {
            clientSession.abortTransaction();
            throw new BadRequestException(e.getMessage());
        }
    }
    /**
     * 分页查询
     *
     * @param dto       查询条件
     * @param s         结果类型
     * @param orderName 排序字段
     * @param <S>       类型
     * @return 分页数据
     */
    public <S extends T> PageVO pagination(PageRequestDTO dto, S s, String orderName, String orderRules) {
        return DataToolUtil.pagination(dto, s.getClass(), mongoTemplate, orderName, orderRules);
    }
    /**
     * 根据表id查询数据详情
     *
     * @param id  表id
     * @param s   数据类型
     * @param <S> 返回数据类型
     * @return 详情信息
     */
    public <S extends T> S getInfoById(ID id, S s) {
        return (S) mongoTemplate.findById(id, s.getClass());
    }
    /**
     * 根据多个表Id查询信息
     *
     * @param ids       多个表id
     * @param s         数据类型
     * @param <S>返回数据类型
     * @return 详情集合
     */
    public <S extends T> List<S> getInfoByIds(List<ID> ids, S s) {
        Query query = Query.query(Criteria.where("_id").in(ids));
        return (List<S>) mongoTemplate.find(query, s.getClass());
    }
    /**
     * 条件查询
     *
     * @param query 查询条件
     * @param s     返回结果
     * @param <S>   结果类型
     * @return 列表
     */
    public <S extends T> List<S> listByQuery(Query query, S s) {
        if (null != query) {
            return (List<S>) mongoTemplate.find(query, s.getClass());
        }
        return (List<S>) mongoTemplate.findAll(s.getClass());
    }
}

四、然后在要使用Mongo的业务类继承这个Service类,像这样:

@Service
@Slf4j
public class ****ServiceImpl extends BaseService<****Entity, String> implements ****Service {
	/**
     * 业务代码
     */
}

类似于使用Mybatis-Plus,要使用MP中Service层级的方法,需要继承ServiceImpl<Mapper, Entity>,像这样:

@Service
@Slf4j
public class ****ServiceImpl extends ServiceImpl<****Mapper, ****Entity> implements *****Service {
    /**
     * 业务代码
     */
}

五、具体在业务类中如何使用,我在这里举一些例子来进行说明,帮助大家最大程度的理解,并可以快速上手使用。

Query与Criteria的关联:

Criteria用来构建查询条件,类似于Mybatis-Plus中的wrapper。而Query是用来组合一个或者多个Criteria的。

在构建多条件查询时,使用下面的语句进行合并,合并有两种情况,一个是criteria是根据where构建,另一种是由and构建:

Query query = new Query();
Criteria criteria1 = Criteria.where("_id").is(id);
query.addCriteria(criteria1);//where形式
Criteria criteria2 = new Criteria();
criteria2.and("_id").is(id);//and形式
query.addCriteria(criteria2);

where是单一条件查询,而and可以构建包含多个条件的查询。

在使用时与Mybatis-Plus一样需要对实体类进行操作,使用@Id注解指定主键Id,使用@Collation注解来对实体类指定名字。

像这样:

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.annotation.Collation;
import java.io.Serializable;
@Data
@Collation("taskLog")
public class TaskLog implements Serializable {
    @Id
    private String logId;
	/*
	 * 其他属性
	 */
}

增:

@Service
@Slf4j
public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService {
    @Autowired
	private MongoTemplate mongoTemplate;
    //增加一个
    @Override
    public void save(TaskLog tasklog) {
        this.insert(tasklog);//实际调用在mongoTemplate的save方法
    }
    //批量增加
    public void insertMany(List<TaskLog> tasklogs) {
    	mongoTemplate.insert(tasklogs, TaskLog.class);
	}
}
/**
 * mongoTemplate中,insert与save的区别:
 * insert()方法:如果tasklog中主键id已经存在了,会抛出异常,表示插入了重复的主键。如果该主键不存在,则会正常生成。
 * 用于生成新的数据
 * 
 * save()方法:如果主键id已经存在,则会覆盖掉原来的数据。
 * 用于更新数据
 */

删:

@Service
@Slf4j
public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService {
    //根据主键id删除一个
    @Override
    public void delete(String logId) {
        this.deleteById(new TaskLog(), logId);
    }
    //根据主键id批量删除
    public void deleteMany(List<String> logIds) {
    	this.deleteByIds(new TaskLog(), logIds);
	}
}
/**
 * 实际使用的是mongoTemplate的remove()方法。
 * Query query = Query.query(Criteria.where("_id").is(id));
 * mongoTemplate.remove(query, s.getClass());
 *
 * Query query = Query.query(Criteria.where("_id").in(ids));
 * mongoTemplate.remove(query, s.getClass());
 */

改:

@Service
@Slf4j
public class TaskLogServiceImpl extends BaseService<TaskLog, String> implements TaskLogService {
    //根据主键id修改
    @Override
    public void update(String logId) {
        this.update(data, data.getId());
    } 
}
   /**
   * 实际使用的是mongoTemplate中的updateFirst()方法
   * Query query = Query.query(Criteria.where("_id").is(id));
   * Update update = new Update();
   * Field[] fields = s.getClass().getDeclaredFields();
   * for (Field field : fields) {
   *     field.setAccessible(true);
   *     //获取属性
   *     String name = field.getName();
   *     //获取属性值
   *     Object value = field.get(s);
   *     if (ObjectUtils.allNotNull(value)) {
   *         update.set(name, value);
   *     }
   * }
   * 利用反射覆盖对应实体类中的每一个字段以及字段值。
   * mongoTemplate.updateFirst(query, update, s.getClass());
   */

简单查:

@Service
@Slf4j
public class AnalysisStudyServiceImpl extends BaseService<AnalysisStudy, String> implements AnalysisStudyService {
    private MongoTemplate mongoTemplate;
    @Resource
    public void setMongoTemplate(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }
    /**
    * 条件查询
    */
    @Override
    public void find(String patientId, String hospitalId) {
        Query query = Query.query(Criteria.where("patientId").is(patientId).and("hospitalId").is(hospitalId));
        List<AnalysisStudy> analysisStudyList = this.listByQuery(query, new AnalysisStudy());
    }  
    //底层使用的是mongoTemplate的find()方法,查询满足query条件的数据。
 	//如果构建的query为null,listByQuery()会调用findAll()方法,查询所有的数据
}

分页查询(重点):

@Service
@Slf4j
public class AnalysisStudyServiceImpl extends BaseService<AnalysisStudy, String> implements AnalysisStudyService {
    @Override
    public PageVO<AnalysisStudy> queryPage(PageRequestDTO pageRequest) {
        return this.pagination(pageRequest, new AnalysisStudy(), "studyDateTime", "asc");
        //第一个参数:查询条件
        //第二个参数:结果类型
        //第三个参数:排序字段
        //第四个参数:排序类型,升序ASC或者降序DESC
    }
}
//BaseService中封装的方法
public <S extends T> PageVO pagination(PageRequestDTO dto, S s, String orderName, String orderRules) {
        return DataToolUtil.pagination(dto, s.getClass(), mongoTemplate, orderName, orderRules);
}
//分页查询的使用封装成了一个工具类
@Slf4j
public class DataToolUtil {
	//防止工具类被实例化
    private DataToolUtil() {
        throw new IllegalStateException("Utility class");
    }			
    /**
     * 在mongodb中分页查询,通过 PageVO 对象封装了结果数量和查询结果
     *
     * @param dto           分页条件
     * @param tClass        返回数据类型
     * @param mongoTemplate 查询模板
     * @param <T>           返回数据
     * @param orderName     排序列名
     * @param orderRules    排序规则 asc 正序 desc 倒序
     * @return 分页结果
     */
    public static <T> PageVO<T> pagination(PageRequestDTO dto, Class<T> tClass,
                                           MongoTemplate mongoTemplate,
                                           String orderName, String orderRules) {
        Query query = handParam(dto);
        if (!ObjectUtils.allNotNull(dto.getCurrentPageNum())) {//没有请求的页码,默认是第一页
            dto.setCurrentPageNum(0);
        }
        if (!ObjectUtils.allNotNull(dto.getSize())) {//没有每页展示的数量,默认是展示10个
            dto.setSize(10);
        }
        //用给定的查询对象query来计算文档数量
        long count = mongoTemplate.count(query, tClass);
        /**
         * dto.getCurrentPageNum():用户请求的页码
         * dto.getSize():每页展示的数量
         * offset用来计算偏移量
         * 例如用户请求的是第2页,(2-1)=1。每页展示10个,1*10=10.则从第11个开始展示。
         */
        int offset = (dto.getCurrentPageNum() - 1) * dto.getSize();
        if (StringUtils.isNotEmpty(orderName) && StringUtils.isNotEmpty(orderRules)) {
            String asc = "asc";//升序排序
            String desc = "desc";//降序排序
            if (asc.equals(orderRules)) {
                //排序逻辑
                query.with(Sort.by(Sort.Order.asc(orderName)));
            }
            if (desc.equals(orderRules)) {
                //排序逻辑
                query.with(Sort.by(Sort.Order.desc(orderName)));
            }
        }
        // 分页逻辑
        /**
         * 举例:跳过10个,每页获取10个,展示11~20的数据。
         */
        query.skip(offset).limit(dto.getSize());
        log.info("querySql>>>>" + query);
        //find()用于获取符合query查询条件的文档列表.tClass是目标实体类
        List<T> list = mongoTemplate.find(query, tClass);
        //创建一个用于封装分页查询结果的对象
        PageVO<T> pageDTO = new PageVO<>();
        //给PageVO对象设置查询到的满足条件的数量。
        pageDTO.setTotalNum(count);
        //将查询到的结果设置到PageVO对象中。
        pageDTO.setResults(list);
        return pageDTO;
    }
    /**
     * 根据传入的查询条件进行 MongoDB 数据库的动态查询
     * @param dto
     * @return
     */
    private static Query handParam(PageRequestDTO dto) {
        Query query = new Query();//创建一个MongoDB查询对象
        List<ParamDTO> params = dto.getParams();//获取传入参数的查询条件列表
        Criteria criteria = new Criteria();//创建MongoDB的Criteria对象,用于构建查询条件
        params.forEach(param -> {//遍历查询条件列表
            String operator = param.getOperator();
            switch (operator) {
                //该语句构建了一个等于(eq)的查询条件,使用 is 方法指定字段等于给定的值。
                case "eq" -> criteria.and(param.getFiled()).is(param.getFiledValue());
                //regex表示使用正则表达式来进行查询,Pattern.compile()来构造一个正则表达式。这个正则表达式是‘^.*value.*$'
                //'^'和‘$'是正则表达式的起始和结束标识。‘.*表示匹配字符0次或者多次'
                //Pattern.CASE_INSENSITIVE表示不区分大小写
                case "like" -> criteria.and(param.getFiled()).regex(Pattern.compile("^.*" + param.getFiledValue()
                        + ".*$", Pattern.CASE_INSENSITIVE));
                case "le" -> criteria.and(param.getFiled()).lt(param.getFiledValue());
                case "ge" -> criteria.and(param.getFiled()).gt(param.getFiledValue());
                //转换成String类型的字符串列表
                case "in" -> {
                    List<String> values = JSON.parseArray(param.getFiledValue().toString(), String.class);
                    criteria.and(param.getFiled()).in(values);
                }
                case "between" -> {
                    if (param.getFiledValue() instanceof JSONArray) {
                        List<Object> list = JSON.parseArray(param.getFiledValue().toString(), Object.class);
                        if (CollUtil.isNotEmpty(list)) {
                            //gte:大于等于
                            //lte:小于等于
                            criteria.and(param.getFiled()).gte(list.get(0)).lte(list.get(1));
                        }
                    }
                }
                default -> {//没匹配任何一个case语句,默认执行default语句。
                }
            }
        });
        query.addCriteria(criteria);//将构建好的 Criteria 对象添加到查询对象中。
        return query;
    }
}
@Data
public class PageVO<T> {
    private Long totalNum;//结果总数
    private List<T>results;//查询结果
}
@Data
@Schema(description = "分页查询条件")
public class PageRequestDTO {
    @Schema(description = "当前页数")
    private Integer currentPageNum;
    @Schema(description = "展示行数")
    private Integer size;
    @Schema(description = "查询条件")
    private List<ParamDTO> params;
}

到此这篇关于MongoDB在Spring Boot中的使用方式举例介绍的文章就介绍到这了,更多相关MongoDB Spring Boot使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于Spring Security的Oauth2授权实现方法

    基于Spring Security的Oauth2授权实现方法

    这篇文章主要介绍了基于Spring Security的Oauth2授权实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • jvm之java类加载机制和类加载器(ClassLoader)的用法

    jvm之java类加载机制和类加载器(ClassLoader)的用法

    这篇文章主要介绍了jvm之java类加载机制和类加载器(ClassLoader)的用法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • 关于Spring Data Jpa 自定义方法实现问题

    关于Spring Data Jpa 自定义方法实现问题

    这篇文章主要介绍了关于Spring Data Jpa 自定义方法实现问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • JVM性能调优实现原理及配置

    JVM性能调优实现原理及配置

    这篇文章主要介绍了JVM性能调优实现原理及配置,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • java String的深入理解

    java String的深入理解

    这篇文章主要介绍了java String的深入理解的相关资料,希望通过本文大家能理解String的用法,需要的朋友可以参考下
    2017-09-09
  • navicatdesignquery.sql.bak系统找不到指定路径错误的解决方法

    navicatdesignquery.sql.bak系统找不到指定路径错误的解决方法

    今天小编就为大家分享一篇关于navicatdesignquery.sql.bak系统找不到指定路径错误的解决方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • SpringBoot+Docker+IDEA实现一键构建+推送、运行、同镜像多容器启动

    SpringBoot+Docker+IDEA实现一键构建+推送、运行、同镜像多容器启动

    这篇文章主要介绍了SpringBoot+Docker+IDEA实现一键构建+推送、运行、同镜像多容器启动,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Spring Bean六种作用域的适用场景和使用方式

    Spring Bean六种作用域的适用场景和使用方式

    本文主要介绍了Spring Bean六种作用域的适用场景和使用方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java实现提取Word文档表格数据

    Java实现提取Word文档表格数据

    使用Java实现Word文档表格数据的提取,可以确保数据处理的一致性和准确性,同时大大减少所需的时间和成本,下面我们来看看具体实现方法吧
    2025-01-01
  • Spring加载XSD文件发生错误的解决方法

    Spring加载XSD文件发生错误的解决方法

    这篇文章主要介绍了Spring加载XSD文件发生错误的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08

最新评论