目录
- 通过 SpringBoot 整合 Spring Data MongoDB 操作 MongoDB 数据库(传统的同步API编程)
- 演示前提:
- 登录单机模式的 mongodb 服务器命令
- 登录【test】数据库的 mongodb 客户端命令
- 登录【admin】数据库的 mongodb 客户端命令
- 代码演示同步API编程
- 实体类
- 配置类
- 方法名关键字查询和@Query查询的接口
- 自定义查询的接口
- 自定义查询接口的实现类
- 要求书名【name】匹配这个这个正则表达式【nameRegex】,且价格【price】大于这个【startPrice】
- 查询价格在这个范围的文档
- 测试方法
- 测试结果
- 完整代码
- Book 实体类
- SyncBookDao 方法名关键字查询和@Query查询接口
- SyncCustomBookDao 自定义查询接口
- SyncCustomBookDaoImpl 自定义查询方法的实现类
- application.properties 配置类
- SyncBookDaoTest 测试类方法
- pom.xml 依赖文件
通过 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 JH2024-03 *///映射到mongodb数据库里面的【books】集合@Document("books")@Datapublic class Book{// id 的类型定义成String,灵活性比较大// @MongoId(FieldType.INT64)//普通的 @Id 注解更合适@Idprivate String id;//给【name】字段建立索引@Indexedprivate String name;@Indexedprivate 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 extendsCrudRepository<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: " />)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{@Autowiredprivate MongoTemplate mongoTemplate;@Overridepublic 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;}@Overridepublic List<Book> findByCustomPrice(double startPrice, double endPrice){//.execute 方法用来执行一个MongoDB查询操作//mongoCollection,来自 MongoDB的驱动 API ,代表一个 collectionList<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=localhostspring.data.mongodb.port=27017spring.data.mongodb.database=testspring.data.mongodb.username=LJHAAAspring.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 JH2024-03 *///表示不要用web环境来进行测试@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)public class SyncBookDaoTest{//依赖注入@Autowiredprivate 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);//自己设置idb.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);}//对文档进行修改@Testpublic 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>