12、MongoDB -- 通过 SpringBoot 整合 Spring Data MongoDB 操作 MongoDB 数据库(传统的同步API编程)

目录

通过 SpringBoot 整合 Spring Data MongoDB 操作 MongoDB 数据库(传统的同步API编程)

演示前提:

登录单机模式的 mongodb 服务器命令

复制代码
mongod.exe --config "E:\install\mongodb\mongodb-4.2.25\mongod.conf"

将 MongoDB 注册成 Windows 服务器

为了方便,我们可以以管理员的身份打开命令行窗口,来执行如下命令可将 MongoDB 注册成 Windows 服务器,就不用每次都用命令启动 mongodb 服务器

复制代码
mongod.exe --config "E:\install\mongodb\mongodb-4.2.25\mongod.conf" --install

登录【test】数据库的 mongodb 客户端命令

复制代码
mongo mongodb://192.168.0.107:27017/test -u LJHAAA -p 123456

登录【admin】数据库的 mongodb 客户端命令

复制代码
mongo mongodb://192.168.0.107:27017/admin -u admin -p 123456

MongoDB--通过SpringBoot整合Spring Data MongoDB操作MongoDB数据库(反应式(异步)编程演示: 方法名关键字查询、@Query查询、自定义查询、样本查询)这篇文章是通过反应式异步编程来演示的。

接下来演示传统的同步API编程:

区别:

异步的DAO接口是继承这个【ReactiveCrudRepository】接口

同步的DAO接口是继承这个【CrudRepository 】接口

代码演示同步API编程

实体类

配置类

方法名关键字查询和@Query查询的接口

自定义查询的接口

自定义查询接口的实现类
要求书名【name】匹配这个这个正则表达式【nameRegex】,且价格【price】大于这个【startPrice】
查询价格在这个范围的文档


测试方法

具体可以参考上一篇的基于反应式的异步API

测试结果

全部都测试通过

mongodb 数据库的books集合的数据

完整代码

Book 实体类

java 复制代码
package cn.ljh.mongoboot.domain;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.index.TextIndexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.FieldType;
import org.springframework.data.mongodb.core.mapping.MongoId;

/**
 * author JH  2024-03
 */

//映射到mongodb数据库里面的【books】集合
@Document("books")
@Data
public class Book
{

    // id 的类型定义成String,灵活性比较大
    // @MongoId(FieldType.INT64)

    //普通的 @Id 注解更合适
    @Id
    private String id;

    //给【name】字段建立索引
    @Indexed
    private String name;

    @Indexed
    private double price;

    private String author;

    //表示 【desc】 字段映射到数据库集合中的【description】字段列
    @Field("description")
    @TextIndexed  //目前全文检索默认不支持中文
    private String desc;

    //无参构造器
    public Book(){}

    //有参构造器
    public Book( String name, double price, String author, String desc)
    {
        this.name = name;
        this.price = price;
        this.author = author;
        this.desc = desc;
    }
}

SyncBookDao 方法名关键字查询和@Query查询接口

java 复制代码
package cn.ljh.mongoboot.dao;

import cn.ljh.mongoboot.domain.Book;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import java.util.List;


//继承这个传统的同步API ---> CrudRepository

//类型参数1:操作的实体类  ; 类型参数2:实体类的主键类型
public interface SyncBookDao extends
        CrudRepository<Book, String>,
        QueryByExampleExecutor<Book>, //样本查询的接口
        SyncCustomBookDao //自定义的查询方法的接口
{
    //===================================================方法名关键字查询(全自动)==========================================
    //返回值为 Flux ,表示接收多个返回值 ;  返回值为 Mono ,表示接收单个返回值

    //根据【关键字】,查询【name】字段包含该关键字的文档
    List<Book> findByName(String name);

    //根据【关键字】,对【price】字段的值进行范围查询
    List<Book> findByPriceBetween(double startPrice, double endPrice);

    //根据【关键字】,通过【通配符】形式查询【author】字段包含该关键字的文档
    List<Book> findByAuthorLike(String authorPattern);

    //根据【关键字】,通过【正则表达式】方式查询【name】字段包含该关键字的文档
    List<Book> findByNameRegex(String name);

    //查询【price】字段的值大于指定参数值(关键字)的文档【有几条】
    Integer countByPriceGreaterThan(double startPrice);

    //===================================================@Query查询(半自动)==============================================

    //通过关键字 term 对文档进行全文检索
    @Query("{$text: {$search: ?0}}")
    List<Book> findByText(String term);


    //通过 【author】字段 和 【price】价格大于指定参数值 来查询文档
    @Query("{author: ?0 ,price:{$gt: ?1}}")
    List<Book> findByQuery(String author, double startPrice);



}

SyncCustomBookDao 自定义查询接口

java 复制代码
package cn.ljh.mongoboot.dao;

import cn.ljh.mongoboot.domain.Book;
import reactor.core.publisher.Flux;

import java.util.List;


//自定义查询方法的接口

public interface SyncCustomBookDao
{

    //要求书名【name】匹配这个这个正则表达式【nameRegex】,且价格【price】大于这个【startPrice】
    List<Book> findByCustomRegexAndPrice(String nameRegex , double startPrice);

    //查询价格在这个范围的文档
    List<Book> findByCustomPrice(double startPrice , double endPrice);


}

SyncCustomBookDaoImpl 自定义查询方法的实现类

java 复制代码
package cn.ljh.mongoboot.dao.impl;

import cn.ljh.mongoboot.dao.SyncCustomBookDao;
import cn.ljh.mongoboot.domain.Book;
import com.mongodb.BasicDBObject;
import com.mongodb.client.MongoCursor;
import org.bson.Document;
import org.bson.types.ObjectId;
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 reactor.core.publisher.Flux;

import java.util.ArrayList;
import java.util.List;


public class SyncCustomBookDaoImpl implements SyncCustomBookDao
{
    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public List<Book> findByCustomRegexAndPrice(String nameRegex, double startPrice)
    {
        //Spring Data MongoDB 提供了一个 Criteria类 来构建 query 这个查询对象

        // where是Criteria类的一个静态方法,用于指定要查询的字段或属性
        // Criteria.where("name")表示对"name"字段进行查询操作,
        // .regex(nameRegex)表示使用正则表达式进行匹配,nameRegex是传入的名称的正则表达式
        // .and("price")表示在上述查询条件的基础上再加入"price"字段的查询条件
        // .gt(startPrice)表示查询大于给定起始价格的值

        Query query = Query.query(
                Criteria
                        .where("name").regex(nameRegex) //查询条件1
                        .and("price").gt(startPrice));  //查询条件2

        //query:代表查询条件 ; Book.class:要查询的实体对象
        List<Book> books = mongoTemplate.find(query, Book.class);

        return books;
    }


    @Override
    public List<Book> findByCustomPrice(double startPrice, double endPrice)
    {
        //.execute 方法用来执行一个MongoDB查询操作
        //mongoCollection,来自 MongoDB的驱动 API ,代表一个 collection

        List<Book> bList = mongoTemplate.execute(Book.class, mongoCollection ->
        {
            //自定义的查询条件是这样的:{price: { $gt: startPrice , $lt:endPrice }}

            //这个BasicDBObject 就是代表查询条件中的一个对象
            BasicDBObject cond = new BasicDBObject();

            //给这个对象设置查询条件,就能得到这个 { $gt: startPrice , $lt:endPrice }
            cond.put("$gt", startPrice);
            cond.put("$lt", endPrice);

            //再创建一个对象
            BasicDBObject bson = new BasicDBObject();
            //再把查询条件设置进去,就得到这个 {price: { $gt: startPrice , $lt:endPrice }} 查询对象
            bson.put("price", cond);

            MongoCursor<Document> docs = mongoCollection.find(bson).iterator();

            //将 docs 里面的每个 document 转换成 Book ,并存到List之后再返回
            List<Book> bookList = new ArrayList<>();

            while (docs.hasNext())
            {
                Document document = docs.next();

                Book book = new Book(
                        (String) document.get("name"),
                        (Double) document.get("price"),
                        (String) document.get("author"),
                        (String) document.get("description"));

                //因为 id 有普通的string类型,也有 objectId 类型,所以需要做判断
                Object id = document.get("_id");
                //如果 id 是 ObjectId 类型; instanceof是Java中的一个运算符,用于检查一个对象是否属于某个特定的类型或其子类型
                if (id instanceof ObjectId)
                {
                    ObjectId obId = (ObjectId) id;
                    //toHexString是ObjectId类的一个方法,用于将ObjectId对象转换为十六进制字符串表示形式
                    book.setId(obId.toHexString());
                } else
                {
                    book.setId((String) id);
                }
                bookList.add(book);
            }
            return bookList;
        });
        return bList;
    }
}

application.properties 配置类

java 复制代码
# 连接mongodb数据库
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=test
spring.data.mongodb.username=LJHAAA
spring.data.mongodb.password=123456

# 指定 spring.data.mongodb 根据实体类Book的字段上的索引注解(@Indexed、@TextIndexed )来创建索引
spring.data.mongodb.auto-index-creation=true

SyncBookDaoTest 测试类方法

java 复制代码
package cn.ljh.mongoboot.dao;

import cn.ljh.mongoboot.domain.Book;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Optional;

/**
 * author JH  2024-03
 */

//表示不要用web环境来进行测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SyncBookDaoTest
{
    //依赖注入
    @Autowired
    private SyncBookDao bookDao;

    //==============================================增删改查==============================================================

    //添加一个文档到books集合里面,该文档的id为自己指定的

    @ParameterizedTest //表示这个方法是一个参数化的测试
    //需要多个参数进行测试,用这个注解
    @CsvSource({
            //因为 mongodb 目前不支持中文进行全文检索,所以把内容写成英文来测试
            "1,火影忍者,100,岸本齐史,this cartoon is very good"
    })
    public void testSaveWithId(String id, String name, double price, String author, String desc)
    {
        Book b = new Book(name, price, author, desc);
        //自己设置id
        b.setId(id);
        Book book = bookDao.save(b);
        System.err.println(book);
    }



    //添加多个文档到books集合里面,该文档的id为mongodb自己指定的

    //表示这个方法是一个参数化的测试
    @ParameterizedTest
    //需要多个参数进行测试,用这个注解
    @CsvSource({
            "家庭教师,200,天野明,aa this cartoon is jiatingjiaoshi",
            "七龙珠,300,鸟山明,aa this cartoon is very qilongzhu",
            "蜡笔小新,400,臼井仪人,bb this cartoon is very labixiaoxin"
    })
    public void testSaveWithId(String name, double price, String author, String desc)
    {
        Book b = new Book(name, price, author, desc);
        Book book = bookDao.save(b);
        System.err.println(book);
    }

    //根据id查询文档
    @ParameterizedTest
    //测试方法只需要一个参数用这个注解
    @ValueSource(strings = {
            "1",
            "65eda80aec60bd4deae6f38b"
    })
    public void testFindById(String id)
    {
        Optional<Book> book = bookDao.findById(id);
        System.err.println(book);
    }

    //对文档进行修改
    @Test
    public void testUpdate()
    {
        Optional<Book> b = bookDao.findById("1");
        //如果Optional对象中存在图书对象,则执行ifPresent中的逻辑
        b.ifPresent(
                //使用Lambda表达式的方式对查找到的图书进行操作
                book ->
                {
                    //进行修改操作
                    book.setName(book.getName() + "AAAAA");
                    //使用.block()方法阻塞当前线程,直到保存操作完成。这样确保更新操作在调用block()之前完成,并且等待操作结果返回
                    bookDao.save(book);
                }
        );
    }


    //根据id查询文档
    @ParameterizedTest
    //测试方法只需要一个参数用这个注解
    @ValueSource(strings = {
            "1",
    })
    public void testDeleteById(String id)
    {
        bookDao.deleteById(id);
    }

    //==============================================方法名关键字查询(全自动查询)============================================

    //根据名字查询文档

    //表示这个方法是一个参数化的测试方法
    @ParameterizedTest
    //只需要一个参数用这个注解
    @ValueSource(strings = {
            "火影忍者",
            "七龙珠"
    })
    public void testFindByName(String name)
    {
        List<Book> books = bookDao.findByName(name);
        books.forEach(System.err::println);
    }


    //根据价格范围查询

    @ParameterizedTest
    @CsvSource({
            "50,250",
            "150,450"
    })
    public void testFindByPriceBetween(double startPrice, double endPrice)
    {
        List<Book> books = bookDao.findByPriceBetween(startPrice, endPrice);
        books.forEach(System.err::println);

    }






    //根据【author】字段进行【通配符】查询
    @ParameterizedTest
    @ValueSource(strings = {
            "天*",
            "岸*"
    })
    public void testFindByAuthorLike(String authorPattern)
    {
        List<Book> books = bookDao.findByAuthorLike(authorPattern);
        books.forEach(System.err::println);
    }


    //通过名字来进行正则表达式查询
    @ParameterizedTest
    @ValueSource(strings = {
            // ^ 符号表示开头,表示必须由【火】字开头; 这个 . 这个点表示匹配任意字符;  $ 符号表示结尾
            "^火.+$",
            //^ . 表示任意符号开头,中间包含【小】,后面的.表示任意符号结尾
            "^.+小.+$"
    })
    public void testFindByNameRegex(String name)
    {
        List<Book> books = bookDao.findByNameRegex(name);
        books.forEach(System.err::println);

    }


    //查询价格大于指定参数值的文档有几条
    @ParameterizedTest
    @ValueSource(doubles = {
            100.0,
            200.0
    })
    public void testCountByPriceGreaterThan(double startPrice)
    {
        System.err.println("price 大于【 " + startPrice + " 】的文档有【 " + bookDao.countByPriceGreaterThan(startPrice) + " 】条");
    }







    //===================================================@Query查询(半自动)==============================================

    //通过关键字 term 对文档进行全文检索

    @ParameterizedTest
    @ValueSource(strings = {
            "good",
            "aa"
    })
    public void testFindByText(String term)
    {
        List<Book> books = bookDao.findByText(term);
        books.forEach(System.err::println);
    }


    //通过 作者 和 价格大于指定参数值 来查询文档
    @ParameterizedTest
    @CsvSource({
            "天野明,50",
            "天野明,500"
    })
    public void testFindByQuery(String author, double startPrice)
    {
        List<Book> books = bookDao.findByQuery(author, startPrice);
        books.forEach(System.err::println);
    }


    //==============================================自定义查询(全手动查询)=================================================


    //要求书名【name】匹配这个这个正则表达式【nameRegex】,且价格【price】大于这个【startPrice】

    @ParameterizedTest
    @CsvSource({
            //^ 表示开头 , 点 . 表示任意字符 ,$ 表示结尾 :全部就是以任意字符开头,然后中间有个【影】字,然后以任意字符结尾
            "^.+影.+$ , 50",
            "^.+教.+$, 50"
    })
    public void testFindByCustomRegexAndPrice(String nameRegex, double startPrice)
    {
        List<Book> books = bookDao.findByCustomRegexAndPrice(nameRegex, startPrice);
        books.forEach(System.err::println);
    }





    //查询价格在这个范围的文档

    @ParameterizedTest
    @CsvSource({
            "99,199",
            "199,399"
    })
    public void testFindByCustomPrice(double startPrice, double endPrice)
    {
        List<Book> books = bookDao.findByCustomPrice(startPrice, endPrice);
        books.forEach(System.err::println);

    }


    //==============================================样本查询=============================================================



    @ParameterizedTest
    @CsvSource({
            "火影忍者,岸本齐史",
            "家庭教师,天野明明明"
    })
    public void testByExanple(String name, String author)
    {

        //构建一个样本查询的对象
        Example<Book> example = Example.of(
                new Book(name, 0.0, author, null),
                ExampleMatcher.matching()
                        .withIgnoreNullValues() //忽略 null 属性
                        .withIgnorePaths("price") //忽略 price 属性
        );

        Iterable<Book> books = bookDao.findAll(example);
        books.forEach(System.err::println);
    }








}

pom.xml 依赖文件

java 复制代码
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
    </parent>

    <groupId>cn.ljh</groupId>
    <artifactId>mongoboot</artifactId>
    <version>1.0.0</version>
    <name>mongoboot</name>


    <properties>
        <java.version>11</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>

        <!-- 同步的 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>

        <!-- 反应式 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
相关推荐
小吴编程之路18 小时前
MySQL 索引核心特性深度解析:从底层原理到实操应用
数据库·mysql
~莫子18 小时前
MySQL集群技术
数据库·mysql
HalvmånEver18 小时前
7.高并发内存池大页内存申请释放以及使用定长内存池脱离new
java·spring boot·spring
凤山老林18 小时前
SpringBoot 使用 H2 文本数据库构建轻量级应用
java·数据库·spring boot·后端
就不掉头发18 小时前
Linux与数据库进阶
数据库
与衫18 小时前
Gudu SQL Omni 技术深度解析
数据库·sql
咖啡の猫19 小时前
Redis桌面客户端
数据库·redis·缓存
oradh19 小时前
Oracle 11g数据库软件和数据库静默安装
数据库·oracle
what丶k19 小时前
如何保证 Redis 与 MySQL 数据一致性?后端必备实践指南
数据库·redis·mysql
_半夏曲19 小时前
PostgreSQL 13、14、15 区别
数据库·postgresql