<h1>Springboot整合Mongodb(含生产案例)</h1><h1>基础语法</h1><h2>插入</h2><p>插入单条</p><p>// 插入一条数据到 "Books" 集合
db.Books.insertOne({
title: "如何使用MongoDB",
author: "IT小辉同学",
year: 2023
})
</p><p>插入多条数据</p><p>// 插入十条数据到 "Books" 集合
db.Books.insertMany([
{ title: "平凡的世界", author: "路遥", year: 1986 },
{ title: "呐喊", author: "鲁迅", year: 1923 },
{ title: "朝花夕拾", author: "鲁迅", year: 1928 },
{ title: "李自成", author: "王安忆", year: 2010 },
{ title: "寻找安详", author: "郭文斌", year: 2000 },
{ title: "推拿", author: "毕飞宇", year: 1999 },
{ title: "苏东坡传", author: "林语堂", year: 2015 },
{ title: "长安的荔枝", author: "马伯庸", year: 2021 },
{ title: "林清玄散文集", author: "林清玄", year: 1988 },
{ title: "橘颂", author: "张炜", year: 2022 }
])
</p><h2>查询</h2><p>//查询全部
db.Books.find()
//条件查询
db.Books.find({author:"鲁迅"})
//模糊查询 /xxx/ 相当于like %xxx%
db.Books.find({author:{set: { year: 2023 } }
)
//修改多条
db.Books.updateMany(
{ title: "橘颂", author: "张炜", year: 1111 },
{ $set: { year: 2222 } }
)
</p><h2>删除</h2><p>//单纯单条
db.Books.deleteOne(
{ title: "橘颂", author: "张炜", year: 2222 }
)
//删除多条
db.Books.deleteMany({ title: "橘颂"})
</p><h1>场景</h1><h2>Springboot整合</h2><p>https://blog.csdn.net/weixin_53742691/article/details/132418525</p><h3>服务引入</h3><h4>1、引入依赖</h4><p><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</p><h4>2、application.yaml配置</h4><p>spring:
data:
mongodb:
host: 127.0.0.1
port: 27017
# 如果有密码的,则需要配置密码
# username: "admin"
# password: "helloAdmin"
database: my_db
</p><h2>各类场景</h2><p>以Book类,作为演示案例</p><p>package com.walker.sample.mongdb.entity;
import com.walker.core.BasePage;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
//文档名称:
@Document(collection = "Books")
@Data
public class Book {
// id
@Id
private String id;
// 主题
private String title;
// 作者
private String author;
// 创建年份
private Integer year;
// 如果需要忽略的字段,则使用@Transient
}
</p><h3>新增数据</h3><p>package com.walker.sample.easyexcel;
import cn.hutool.core.util.RandomUtil;
import com.walker.sample.mongdb.entity.Book;
import com.walker.sample.mongdb.utils.RandomNameUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Slf4j
@SpringBootTest
public class MongdbTest {
// 1、引入template依赖
@Autowired
private MongoTemplate mongoTemplate;
/
* author:walker
* time: 2024/6/5
* description: 插入数据
/
@Test
public void addData(){
List<Book> books = new ArrayList<>();
Book book;
// 2、构建数据
for (int i = 0; i < 10000; i++) {
book = new Book();
book.setTitle(RandomNameUtil.getName()+"的书"+i);
book.setAuthor(RandomNameUtil.getName());
book.setYear(Integer.parseInt(RandomUtil.randomNumbers(5)));
books.add(book);
}
// 3、使用insert进行数据的插入
mongoTemplate.insert(books,Book.class);
}
}
</p><p>插入数据结果:</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/26116386-6bc5a5111762f811.jpeg" img-data="{"format":"jpeg","size":84379,"width":841,"height":333,"space":"srgb","channels":3,"depth":"uchar","density":72,"chromaSubsampling":"4:2:0","isProgressive":false,"hasProfile":false,"hasAlpha":false}" contenteditable="false" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><p>但是可以发现,多了一个_class字段</p><p>作用:是为了方便处理Pojo中存在继承的情况,增加系统的扩展性的</p><p>具体可以参考:</p><p>MongoDB中_class字段的作用 - 上帝爱吃苹果-Soochow - 博客园</p><p>注意:</p><ol><li>RandomNameUtil类:主要是用来创建名称</li></ol><p>package com.walker.sample.mongdb.utils;
/
*
* 随机姓名
/
public class RandomNameUtil {
// 姓氏池
private static final String XING = "赵钱孙李周吴郑王";
// 名字池
private static final String MING = "三四五六七八建国强国富民少年强则中国强";
public static String getName() {
// 获取姓氏池的随机下标并随机获取一个姓氏
char xing = XING.charAt((int) (Math.random() * XING.length()));
// 创建一个可扩容字符串
StringBuilder userName = new StringBuilder().append(xing);
// 随机生成1或2,决定名字长度
int mingLength = 1 + (int) (Math.random() * 2);
int mingPoolLength = MING.length();
for (int i = 0; i < mingLength; i++) {
// 获取名字池的随机下标并随机获取一个名字字符并拼接
char ming = MING.charAt((int) (Math.random() * mingPoolLength));
userName.append(ming);
}
return userName.toString();
}
}
</p><h3>分页查询</h3><p>这个应该是我们实际应用中应用的比较多的了。</p><p>分别有多条件结合查询、排序、分页、总数查询等,那么下边将使用一个查询接口作为案例。</p><h4>查询controller和逻辑</h4><p>package com.walker.sample.mongdb;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.walker.core.TableDataInfo;
import com.walker.mongdb.utils.MyCriteria;
import com.walker.mongdb.utils.MyQuery;
import com.walker.sample.mongdb.entity.Book;
import com.walker.sample.mongdb.entity.BookForm;
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 org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/book")
public class BookController {
// 1、引入模板类
@Autowired
private MongoTemplate mongoTemplate;
@GetMapping("/list")
public TableDataInfo<Book> list(BookForm form) {
// 2、创建查询类
Query query = new Query();
// 3、创建标准/条件
Criteria criteria = new Criteria();
// 因为是多条件结合,所以使用数组进行存储
List<Criteria> criteriaList = new ArrayList<>();
// 模块查询title、
if (StrUtil.isNotEmpty(form.getTitle())) {
criteriaList.add(MyCriteria.like(Book::getTitle, form.getTitle()));
}
// 精准查询year
if (form.getYear() != null) {
criteriaList.add(MyCriteria.is(Book::getYear, form.getYear()));
}
// 根据year查询in
if (CollUtil.isNotEmpty(form.getYears())) {
criteriaList.add(MyCriteria.in(Book::getYear, form.getYears()));
}
// 将条件放入一个大的criteria中
criteria.andOperator(criteriaList);
// query添加criteria
query.addCriteria(criteria);
// 计算总数
Long count = mongoTemplate.count(query, Book.class);
// 设置跳页
MyQuery.setPageNumAndSize(query, form.getPageNum(), form.getPageSize());
// 排序
MyQuery.sortAsc(query, Book::getYear);
// 分页查询
List<Book> books = mongoTemplate.find(query, Book.class);
return new TableDataInfo<>(count, books);
}
}
</p><h4>涉及类</h4><p>1、查询参数</p><p>用于请求参数的传递</p><p>package com.walker.sample.mongdb.entity;
import com.walker.core.BasePage;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
@Data
public class BookForm extends BasePage{
// 主题
private String title;
// 作者
private String author;
// 创建年份
private Integer year;
// 查询的years的条件
@Transient
private List<Integer> years;
}
</p><ol><li>基础分页参数</li></ol><p>统一封装,如果需要使用的参数直接继承即可</p><p>package com.walker.core;
import lombok.Data;
@Data
public class BasePage {
/
* 页码
/
private Integer pageNum;
/
* 页数
/
private Integer pageSize;
}
</p><ol><li>Criteria工具类</li></ol><p>package com.walker.mongdb.utils;
import com.walker.mongdb.annotation.SFunction;
import org.springframework.data.mongodb.core.query.Criteria;
public class MyCriteria {
/
* 模糊查询
/
public static <T> Criteria like(SFunction<T> getField, Object value){
String fieldName = FieldUtils.getFieldName(getField);
return Criteria.where(fieldName).regex(".?\" + value + ".");
}
/
* 精确查询
/
public static <T> Criteria is(SFunction<T> getField,Object value){
String fieldName = FieldUtils.getFieldName(getField);
return Criteria.where(fieldName).is(value);
}
/
* 多个查询
/
public static <T> Criteria in(SFunction<T> getField,Object value){
String fieldName = FieldUtils.getFieldName(getField);
return Criteria.where(fieldName).in(value);
}
}
</p><ol><li>Query封装类</li></ol><p>package com.walker.mongdb.utils;
import com.walker.mongdb.annotation.SFunction;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Query;
public class MyQuery {
/
* 设置页码和页数
/
public static void setPageNumAndSize(Query query, Integer pageNum, Integer pageSize) {
if(pageNum==null) pageNum=1;
if(pageSize==null) pageSize=10;
query.skip((long) (pageNum - 1) pageSize);
query.limit(pageSize);
}
/
* 正序
/
public static <T> void sortAsc(Query query, SFunction<T> getField) {
String fieldName = FieldUtils.getFieldName(getField);
Sort sort = Sort.by(Sort.Direction.ASC, fieldName);
query.with(sort);
}
/*
* 倒序
*/
public static <T> void sortDesc(Query query, SFunction<T> getField) {
String fieldName = FieldUtils.getFieldName(getField);
Sort sort = Sort.by(Sort.Direction.DESC, fieldName);
query.with(sort);
}
}
</p><ol><li>属性相关类,为了能够使用Stream方法去获取类的名称,减少魔法值的使用</li></ol><p>package com.walker.mongdb.utils;
import com.walker.mongdb.annotation.SFunction;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class FieldUtils {
private static final Map<SFunction<?>, Field> FUNCTION_CACHE = new ConcurrentHashMap<>();
public static <T> String getFieldName(SFunction<T> function) {
Field field = FieldUtils.getField(function);
return field.getName();
}
public static <T> Field getField(SFunction<T> function) {
return FUNCTION_CACHE.computeIfAbsent(function, FieldUtils::findField);
}
public static <T> Field findField(SFunction<T> function) {
// 第1步 获取SerializedLambda
final SerializedLambda serializedLambda = getSerializedLambda(function);
// 第2步 implMethodName 即为Field对应的Getter方法名
final String implClass = serializedLambda.getImplClass();
final String implMethodName = serializedLambda.getImplMethodName();
final String fieldName = convertToFieldName(implMethodName);
// 第3步 Spring 中的反射工具类获取Class中定义的Field
final Field field = getField(fieldName, serializedLambda);
// 第4步 如果没有找到对应的字段应该抛出异常
if (field == null) {
throw new RuntimeException("No such class 「"+ implClass +"」 field 「" + fieldName + "」.");
}
return field;
}
static Field getField(String fieldName, SerializedLambda serializedLambda) {
try {
// 获取的Class是字符串,并且包名是“/”分割,需要替换成“.”,才能获取到对应的Class对象
String declaredClass = serializedLambda.getImplClass().replace("/", ".");
Class<?>aClass = Class.forName(declaredClass, false, ClassUtils.getDefaultClassLoader());
return ReflectionUtils.findField(aClass, fieldName);
}
catch (ClassNotFoundException e) {
throw new RuntimeException("get class field exception.", e);
}
}
static String convertToFieldName(String getterMethodName) {
// 获取方法名
String prefix = null;
if (getterMethodName.startsWith("get")) {
prefix = "get";
}
else if (getterMethodName.startsWith("is")) {
prefix = "is";
}
if (prefix == null) {
throw new IllegalArgumentException("invalid getter method: " + getterMethodName);
}
// 截取get/is之后的字符串并转换首字母为小写
return Introspector.decapitalize(getterMethodName.replace(prefix, ""));
}
static <T> SerializedLambda getSerializedLambda(SFunction<T> function) {
try {
Method method = function.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(Boolean.TRUE);
return (SerializedLambda) method.invoke(function);
}
catch (Exception e) {
throw new RuntimeException("get SerializedLambda exception.", e);
}
}
}
</p><p>函数接口</p><p>package com.walker.mongdb.annotation;
import java.io.Serializable;
@FunctionalInterface
public interface SFunction<T> extends Serializable {
Object apply(T t);
}
</p><h4>测试</h4><p>具体就不演示了,可以使用各种参数进行测试</p><div class="image-package"><img src="https://upload-images.jianshu.io/upload_images/26116386-8f55b68f182d12e0.jpeg" img-data="{"format":"jpeg","size":31805,"width":1000,"height":584,"space":"srgb","channels":3,"depth":"uchar","density":72,"chromaSubsampling":"4:2:0","isProgressive":false,"hasProfile":false,"hasAlpha":false}" contenteditable="false" class="uploaded-img" style="min-height:200px;min-width:200px;" width="auto" height="auto"/>
</div><h3>事务</h3><p>确保你的MongoDB服务器版本至少是4.0以支持事务。此外,在使用事务时,请注意以下几点:</p><ol><li>你的MongoDB集合必须是事务兼容的。</li></ol><ol><li>所有参与事务的集合必须在同一个数据库中。</li></ol><ol><li>在事务方法中,不要直接使用MongoTemplate的execute方法来执行自定义操作。</li></ol><ol><li>事务方法不能是非事务性的(即不能是@Transactional(propagation = Propagation.NOT_SUPPORTED))。</li></ol><ol><li>如果使用MongoTemplate进行查询,确保查询是事务兼容的。</li></ol><p>实现方案:</p><p>1、服务开启事务 使用@EnableTransactionManagement注解</p><p>package com.walker.sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
//开启事务
@EnableTransactionManagement
@SpringBootApplication
public class WalkerSampleApplication {
public static void main(String[] args) {
SpringApplication.run(WalkerSampleApplication.class, args);
}
}
</p><h3>忽略类中的字段</h3><p>使用@Transient</p><p>package com.walker.sample.mongdb;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
//文档名称:
@Document(collection = "Books")
@Data
public class Book {
@Id
private String id;
private String title;
private String author;
private Integer year;
@Transient
private List<Integer> years;
}
</p>
Springboot整合Mongodb(含生产案例)
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 摘要:开始前,建议大家去了解以下文章,当然不看也没问题: MongoDB从入门到“精通”之简介和如何安装 Mong...
- MongoDB简介 MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的...
- SpringBoot 2.X 整合MongoDB案例 官方文档介绍 官方各个版本说明: 简介 Mongodb是为快...
- Spring基于注解的缓存 对于缓存声明,spring的缓存提供了一组java注解: @Cacheable:触发缓...