首页 > 数据库 >使用 SpringData 操作 Mongodb

使用 SpringData 操作 Mongodb

时间:2023-08-15 21:12:36浏览次数:40  
标签:SpringData Mongodb springframework mongodb Employee org import 操作 data

本篇博客主要介绍 SpringBoot 如何通过 SpringData 操作 Mongodb。在上篇博客部署的 mongodb 为了方便,在 admin 库中创建了一个 root 角色的账号,使用这个账号具有最高权限,可以访问和操作任何库。在实际项目中强烈建议为每个 mongodb 数据库创建一个低权限角色的用户,比如具有 readwrite 角色的用户。

有关 mongodb 支持的数据类型,以及索引视图等等,这里不做介绍,熟悉关系型数据库的小伙伴,对此肯定很容易入门,具体细节可以参考官网。本篇博客以 Demo 代码的方式介绍如何操作 mongodb,在博客最后会提供源代码下载。

Mongodb 的中文官网地址:https://www.mongodb.com/zh-cn


一、工程搭建

我的虚拟机 ip 地址是:192.168.136.128,在上篇博客中通过 DockerCompose 部署的 mongodb 时,创建了一个 root 角色的账号是 jobs ,密码是 123456,本篇博客的 Demo 代码,连接这个 mongodb 进行操作演示。

搭建一个 SpringBoot 工程,结构如下所示:

image

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:[email protected]: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);
        }
    }

    //删除【薪水最高】的老油条员工
    @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 中具体的数据,下图是操作过程中的截图:

image


三、事务支持

为了能够让 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

标签:SpringData,Mongodb,springframework,mongodb,Employee,org,import,操作,data
From: https://www.cnblogs.com/studyjobs/p/17632443.html

相关文章

  • 求有操作的各位数和
    如何求一个数的位数最大和题目思路给定一个数,从他最小位到最高位进行操作,对于每一位上的数字,可以选择让他加1,减1,或者是保持不变,求位数最大和(对于整体其实减去了一个10的多少次方)思路要使得每一位的总和最大,我们可以对每一位进行一个判断我们只需要对0和9这两个数字进行判断即......
  • 【LeetCode 571. 给定数字的频率查询中位数】WITH RECURSIVE实现Tally的逆操作
    目录题目地址代码题目地址https://leetcode.cn/problems/find-median-given-frequency-of-numbers/description/代码WITHRECURSIVERecCTEAS(SELECTnum,frequency-1asremaining_frequencyFROMNumbersWHEREfrequency>0UNIONALLSELECTn......
  • JavaScript基础:学习JavaScript语言的基本语法和常用操作,了解网页交互的基本原理
    JavaScript是一种广泛应用于网页开发中的脚本语言,它可以与HTML和CSS一起使用,实现网页交互及动态效果。以下是JavaScript的基本语法和常用操作:变量声明:使用var、let或const关键字声明变量。varname="John";letage=25;constPI=3.14;数据类型:包括字符串、数字、布......
  • SPI与SDIO两种操作方式读写SD卡速度测试
    SPI与SDIO两种操作方式读写SD卡速度测试 目前嵌入式应用中,若要用到SD卡,一般有两种操作方式可选:一种是SPI操作SD卡的方式;一种是SDIO操作SD卡的方式。一般是SPI操作SD卡方式使用较多,因为一般正常的MCU都带有SPI接口,但SDIO接口一般存在于大容量MCU中,中小容量的MCU一般不带有SDI......
  • 【Alibaba中间件技术系列】「RocketMQ技术专题」让我们一起实践RocketMQ的服务搭建及
    推荐超值课程:点击获取RocketMQ是一款分布式、队列模型的消息中间件,具有以下特点1、能够保证严格的消息顺序2、提供丰富的消息拉取模式3、高效的订阅者水平扩展能力4、实时的消息订阅机制5、亿级消息堆积能力搭建一个双节点的RocketM环境背景:虚拟机:vmware12操作系统:ce......
  • 视频监控管理平台EasyCVR视频平台通过navicat修改登录密码的具体操作步骤
    TSINGSEE青犀视频监控管理平台EasyCVR可以根据不同的应用场景需求,让平台在内网、专网、VPN、广域网、互联网等各种环境下进行音视频的采集、接入与多端分发。在视频能力上,平台可实现视频实时直播、云端录像、云存储、回放与检索、告警上报、视频快照、视频转码与分发、平台级联等。......
  • 0基础转行产品经理可行吗?如何操作才靠谱?
    在多年的产品经验中,遇到了很多朋友同事咨询转行做产品的可行性,在沟通的过程中,大家都普遍认为零基础转行产品,对于一个已经有工作经验的人来说,是不是需要付出很大的努力,之前走的路就全白费了,在此我认为零基础转行产品经理完全可行。   首先我们需要了解产品经理是一个什么样的职位......
  • 【Linux技术专题】「必备基础知识」带你仔细梳理一下平时排查问题查询日志的基本操作
    Linux文件与目录管理了解和熟悉Linux系统的文件存储结构、权限设置和命令行工具,将有助于快速定位和解决与日志相关的问题,提高系统管理员和开发人员的工作效率。文件路径:Linux系统的日志文件按规定的目录结构组织。了解不同日志文件的存储位置和命名规则,可更快地找到所需的日志文件......
  • EAS_客户端设置按钮和菜单栏操作选项隐藏
    我们要想隐藏客户端的按钮或者菜单,防止操作,可以在listUI或者EditUI中onload()方法中使用下列方式来设置控件是否显示和是否可用,控件名可通过dep查看或者通过shift+alt+d来查看控件publicvoidonload(){//禁用按钮this.btnSave.setVisible(false);......
  • Python中对文件进行操作
    对于Python来说,文件处理绝对是一个常见的处理,读取文件、写入文件、生成文件……文件操作贯穿python变成始终。本篇文章将总结一下在平时编程过程中,常用的文件操作。以下将按照增删改查的顺序,对文件以及目录操作进行总结。新建文件和目录importos#新建文件new_file_path......