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 实体类

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查询接口

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 自定义查询接口

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 自定义查询方法的实现类

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 配置类



# 连接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 测试类方法

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 依赖文件

<?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>



猜你喜欢

转载自blog.csdn.net/weixin_44411039/article/details/136625574