目录
- 通过 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 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>