多线程按序执行三个不同的线程 A、B、C 将会共用一个 Foo 实例。线程 A 将会调用 first() 方法线程 B 将会调用 second() 方法线程 C 将会调用 third() 方法请设计程序，以确保 second() 方法在 first() 方法之后被执行，third() 方法在 second() 方法之后被执行。解决思路定义标记，如对应线程的标志为可运行时，线程才可执行。AtomicInteger 是 Java 并发包 (java.util.concurrent.atomic) 中的一个原子类，用于在多线程环境下安全地操作单个 int 类型的值。它提供了一种无锁的线程安全整数操作方式，性能通常优于使用同步锁的方法。以下是 AtomicInteger 的详细介绍：基本概念：AtomicInteger 封装了一个 int 值，所有对这个值的操作都是原子的。它使用了 CPU 的 CAS（Compare-And-Swap）指令来确保操作的原子性。适用于需要频繁更新单个整数值的多线程场景。主要方法：get(): 获取当前值set(int newValue): 设置为给定值getAndSet(int newValue): 设置新值并返回旧值compareAndSet(int expect, int update): 如果当前值等于预期值，则更新为新值getAndIncrement(): 自增并返回旧值getAndDecrement(): 自减并返回旧值incrementAndGet(): 自增并返回新值decrementAndGet(): 自减并返回新值getAndAdd(int delta): 加上给定值并返回旧值addAndGet(int delta): 加上给定值并返回新值使用场景：计数器（如请求计数、并发访问计数）序列号生成无锁算法中的版本控制使用CountDownLatch ，CountDownLatch 是 Java 并发包 (java.util.concurrent) 中的一个同步工具类，用于协调多个线程之间的同步。它允许一个或多个线程等待，直到其他线程完成一组操作后才继续执行基本概念：CountDownLatch 维护一个计数器，这个计数器被初始化为一个正整数，表示需要等待的事件数量。当事件发生时，计数器会递减。线程可以调用 await() 方法阻塞等待，直到计数器变为零。主要方法：countDown()：使计数器减 1。当计数器达到零时，所有等待的线程将被释放。await()：使当前线程等待，直到计数器为零，或者线程被中断。await(long timeout, TimeUnit unit)：带超时的等待。getCount()：获取当前的计数。使用场景：确保某个计算在其需要的所有资源都被初始化之后才执行。确保某个服务在其依赖的所有其他服务都已经启动之后才启动。等待多个并行执行的任务全部完成。使用原子变量进行标记public class ThA { static class Foo { public Foo() { } private final AtomicInteger third = new AtomicInteger(0); private final AtomicInteger second = new AtomicInteger(0); public void first() { System.out.println("first"); second.incrementAndGet(); } public void second() { while (second.get() != 1) { } System.out.println("second"); second.incrementAndGet(); } public void third() { while (third.get() != 1) { } System.out.println("third"); } } public static void main(String[] args) { long startTime = System.currentTimeMillis(); Foo foo = new Foo(); Thread thread1 = new Thread(foo::first); Thread thread2 = new Thread(foo::second); Thread thread3 = new Thread(foo::third); thread1.start(); thread2.start(); thread3.start(); try { thread1.join(); thread2.join(); thread3.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } long endTime = System.currentTimeMillis(); long duration = endTime - startTime; System.out.println("开始: " + startTime); System.out.println("结束: " + endTime); System.out.println("程序执行时间（毫秒）: " + duration); } }CountDownLatch同步工具类public class ThA { static class Foo { public Foo() { } private final CountDownLatch thirdDown = new CountDownLatch(1); private final CountDownLatch secondDown = new CountDownLatch(1); public void first() { System.out.println("first"); secondDown.countDown(); } public void second() { try { secondDown.await(); System.out.println("second"); secondDown.countDown(); } catch (InterruptedException e) { throw new RuntimeException(e); } } public void third() { try { thirdDown.await(); System.out.println("third"); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static void main(String[] args) { long startTime = System.currentTimeMillis(); Foo foo = new Foo(); Thread thread1 = new Thread(foo::first); Thread thread2 = new Thread(foo::second); Thread thread3 = new Thread(foo::third); thread1.start(); thread2.start(); thread3.start(); try { thread1.join(); thread2.join(); thread3.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } long endTime = System.currentTimeMillis(); long duration = endTime - startTime; System.out.println("开始: " + startTime); System.out.println("结束: " + endTime); System.out.println("程序执行时间（毫秒）: " + duration); } }
Linux服务器部署Java项目后无法渲染验证码
安装字体包安装字体在哪个目录都可以输入的命令yum install fontconfig -y fc-cache --force安装完后在/usr/shared目录就可以看到fonts和fontconfig两个目录。然后重启项目，验证码能正常获取了并且后端也不再报错了。
Java中如何将Markdown转成Word？1、Maven依赖 com.vladsch.flexmark flexmark-all 0.64.8 org.apache.poi poi 5.3.0 org.apache.poi poi-ooxml 5.3.0 org.apache.poi poi-ooxml-schemas 4.1.2 2、工具类package com.pq.chat.ai.core.utils; import cn.hutool.core.util.IdUtil; import com.vladsch.flexmark.ext.tables.TablesExtension; import com.vladsch.flexmark.html.HtmlRenderer; import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.data.MutableDataSet; import lombok.extern.slf4j.Slf4j; import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import java.io.ByteArrayInputStream; import java.io.FileOutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; /** * Word工具类 * @author THF * @date 2024/9/13 14:15 */ @Slf4j public class WordUtil { /** * 将MarkDown转换成HTML * * @param markdown Markdown * @return HTML */ public static String markdownToHtml(String markdown) { // 设置解析器配置 MutableDataSet options = new MutableDataSet(); options.set(Parser.EXTENSIONS, List.of(TablesExtension.create())); // 创建Markdown解析器和HTML渲染器 Parser parser = Parser.builder(options).build(); HtmlRenderer renderer = HtmlRenderer.builder(options).build(); // 将Markdown转换为HTML return renderer.render(parser.parse(markdown)); } /** * HTML转换成Word * * @param html HTML * @return WORD文件 */ public static String htmlToWord(String html) { try { String html1 = " " + " " + " " + " " + // 你的内容 html + " "; // 文件名称 String fileName = IdUtil.fastSimpleUUID() + ".doc"; //目标路径 Path targetPath = Paths.get(FileProperties.BASE_PATH, fileName); Files.createDirectories(targetPath.getParent()); // 保存doc文档 FileOutputStream outputStream = new FileOutputStream(targetPath.toFile()); // 生成doc格式的word文档，需要手动改为docx byte[] by = html1.getBytes(StandardCharsets.UTF_8); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(by); POIFSFileSystem poifsFileSystem = new POIFSFileSystem(); DirectoryEntry directoryEntry = poifsFileSystem.getRoot(); directoryEntry.createDocument("WordDocument", byteArrayInputStream); poifsFileSystem.writeFilesystem(outputStream); byteArrayInputStream.close(); //关闭输出流 outputStream.close(); return targetPath.toFile().getAbsolutePath(); } catch (Exception e) { throw new RuntimeException(e); } } } 3、使用//将Markdown转换成HTMl String html = WordUtil.markdownToHtml(markdown.toString()); //将HTML转换成WORD，返回文件路径 String filePath = WordUtil.htmlToWord(html);
Nginx安装并配置国家/城市黑白名单限制访问
下载Nginx包Nginx官网下载地址安装GCC等环境# 安装gcc，源码编译依赖 gcc 环境 yum -y install gcc-c++ # 安装pcre，pcre是一个perl库，包括perl兼容的正则表达式库，nginx的http模块使用pcre来解析正则表达式，所以需要安装pcre库 yum install -y pcre pcre-devel # 安装zlib，zlib 库提供了很多种压缩和解压缩的方式，nginx 使用 zlib 对 http 包的内容进行 gzip yum install -y zlib zlib-devel # 安装OpenSSL库 yum install -y openssl openssl-devel下载Nginx-Geo模块下载地址解压到Linux服务器中例如：/env/nginx_lib/ngx_http_geoip2_module-3.4下载国家/城市数据库国家数据库城市数据库解压到Linux服务器中例如：/env/nginx_lib/GeiLite2-Country.mmdb国家数据库文件名：GeiLite2-Country.mmdb城市数据库文件名：GeiLite2-City.mmdb编译安装# 到下载的Nginx目录下执行命令 ./configure --prefix=[Nginx安装目录] --with-http_ssl_module --add-module=[ngx_http_geoip2_module模块文件路径] && make -j 4 && make install #到安装目录启动Nginx ./nginx #例如 # ./configure --prefix=/env/nginx --with-http_ssl_module --add-module=/env/nginx_lib/ngx_http_geoip2_module-3.4 && make -j 4 && make install ChatGPT解释这行命令是用于配置、编译和安装 Nginx 的。让我们分解一下每个部分的含义： 1. `./configure --prefix=/env/nginx --with-http_ssl_module --add-module=/env/nginx_lib/ngx_http_geoip2_module-3.4`: - `./configure`: 这是一个脚本，用于检查系统环境并配置 Nginx 的构建选项。 - `--prefix=/env/nginx`: 这个选项指定了 Nginx 将被安装到的目录。在这种情况下，Nginx 将被安装到 `/env/nginx` 目录下。 - `--with-http_ssl_module`: 这个选项启用了 HTTP SSL 模块，使 Nginx 支持 HTTPS。 - `--add-module=/env/nginx_lib/ngx_http_geoip2_module-3.4`: 这个选项用于添加一个额外的模块，在这里是 `ngx_http_geoip2_module`，该模块用于处理基于 GeoIP2 数据库的地理定位功能。模块的路径是 `/env/nginx_lib/ngx_http_geoip2_module-3.4`。 2. `&& make -j 4`: - `&&`: 这是一个逻辑操作符，用于在前一条命令成功后执行下一条命令。 - `make -j 4`: 这个命令用于并行编译 Nginx 代码。`-j 4` 表示使用 4 个并行线程来加快编译速度。 3. `&& make install`: - `make install`: 这个命令用于将编译完成的 Nginx 安装到前面指定的目录 `/ent-test/nginx` 中。 总结来说，这行命令是在配置、编译和安装 Nginx，同时启用了 SSL 支持并添加了 GeoIP2 模块。限制国家访问nginx.conf# 配置到http代码段中，/env/nginx_lib/GeoLite2-Country.mmdb为国家数据库路径 # 配置 GeoIP2 模块，指定使用的 GeoLite2-Country.mmdb 数据库 geoip2 /env/nginx_lib/GeoLite2-Country.mmdb { # 每5分钟自动重新加载数据库 auto_reload 5m; # 通过访问者IP获取国家代码，默认值为US（美国） $geoip2_data_country_code default=US source=$remote_addr country iso_code; # 通过访问者IP获取城市名称，默认值为Toronto（多伦多） $geoip2_data_city_name default=Toronto source=$remote_addr city names en; } # 定义自定义的日志格式，记录访客的地理位置和请求信息 log_format custom_format_country '$http_x_forwarded_for - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$geoip2_data_country_code" "$block_country"'; # 配置到server中 # 将日志记录到 logs/host.access.log 文件中，使用自定义日志格式 custom_format_country access_log logs/host.access.log custom_format_country; # 映射国家代码到阻止标志位 map $geoip2_data_country_code $block_country { # 默认值为0，不阻止 default 0; # 如果国家代码为CN（中国），则设置阻止标志位为1 "CN" 1; } # 如果阻止标志位为1，则返回403 Forbidden，阻止访问 if ($block_country = 1) { return 403; } 限制区域访问nginx.conf# 配置到http代码段中，/env/nginx_lib/GeoLite2-City.mmdb为国家数据库路径 # 配置 GeoIP2 模块，指定使用的 GeoLite2-City.mmdb 数据库 geoip2 /env/nginx_lib/GeoLite2-City.mmdb { # 每5分钟自动重新加载数据库 auto_reload 5m; # 通过访问者IP获取国家代码，默认值为US（美国） $geoip2_data_country_code default=US source=$remote_addr country iso_code; # 通过访问者IP获取城市名称，默认值为Toronto（多伦多） $geoip2_data_city_name default=Toronto source=$remote_addr city names en; } # 定义自定义的日志格式，记录访客的地理位置和请求信息 log_format custom_format_city '$http_x_forwarded_for - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$geoip2_data_city_code" "$block_city"'; # 配置到server中 # 将日志记录到 logs/host.access.log 文件中，使用自定义日志格式 custom_format_city access_log logs/host.access.log custom_format_city; # 映射城市代码到阻止标志位 map $geoip2_data_city_code $block_city { # 默认值为0，不阻止 default 0; # 如果城市代码为珠海（珠海），则设置阻止标志位为1 "Zhuhai" 1; } # 如果阻止标志位为1，则返回403 Forbidden，阻止访问 if ($block_country = 1) { return 403; } 测试查找日志文件命令：find / -name host.access.log 定位到日志目录并访问：tail -f host.access.log 访问nginx.conf配置文件server代码段的端口并查看日志文件，如果在配置文件中配置了CN为1，则使用中国地区IP地址访问Nginx则返回403
配置Java环境变量在/etc/profile文件最下方追加下面两行export JAVA_HOME=/env/jdk/jdk-17.0.8 export PATH=$PATH:$JAVA_HOME/bin刷新配置文件：source /etc/profile安装字体包安装字体在哪个目录都可以输入的命令yum install fontconfig -y fc-cache --force安装完后在/usr/shared目录就可以看到fonts和fontconfig两个目录。然后重启项目，验证码能正常获取了并且后端也不再报错了。
SpringBoot实现数据下资源的永久地址目的：实现数据下资源的永久地址，无论这条数据下文件资源被替换成何种文件，但是预览地址始终不变/** * 预览或下载文件 * @param id 资源id * @return 文件 */ @GetMapping("/preview/{id}") public ResponseEntity<org.springframework.core.io.Resource> preview(@PathVariable("id") Long id) { LayoutResource layoutResource = layoutResourceService.getById(id); if(ObjectUtil.isEmpty(layoutResource)){ throw new ServiceException("资源不存在"); } SysOssVo sysOssVo = sysOssService.getById(layoutResource.getOssId()); String contentType = getContentType(sysOssVo.getUrl()); InputStream inputStream = sysOssService.getObjectContent(layoutResource.getOssId()); boolean hasImage = isImage(sysOssVo.getFileName()); if (contentType == null) { contentType = "application/octet-stream"; } return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, hasImage ? "inline" : "attachment; filename=\"" + java.net.URLEncoder.encode(sysOssVo.getOriginalName(),StandardCharsets.UTF_8) + "\"") .contentType(MediaType.parseMediaType(contentType)) .body(new InputStreamResource(inputStream)); }/** * 获取文件类型 * @param urlString URL地址 * @return 文件类型 */ private String getContentType(String urlString){ String contentType = null; try { URL url = new URL(urlString); URLConnection connection = url.openConnection(); connection.setUseCaches(false); connection.connect(); contentType = connection.getContentType(); } catch (Exception e) { e.printStackTrace(); } return contentType; }
Win10/11下安装WSL并修改WSL默认安装目录到其他盘（转载）
转载至 https://blog.csdn.net/farer_yyh/article/details/133934904
Nuxt教程官网：https://nuxt.com/1、初始化项目如初始化项目失败请在WIndows的hosts文件中添加如下代码185.199.108.133 raw.githubusercontent.com2、认识项目文件.nuxt：运行项目生成的文件nuxt.config.ts：配置文件// https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ // 是否开启SSR服务端渲染，默认为true ssr:true, devtools: { enabled: true } })Nuxt自带是基于文件的路由系统，无需安装vue-router3、SEO优化通过设置网页 title 和 description 等SEO优化信息，由服务端渲染，可提高网页在搜索引擎页面中的排名和可见性app.vue 首页 视频 <NuxtPage />
Java给图片添加水印1、平铺水印package com.ruoyi.system.utils; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; public class ImageUtil { // 水印透明度 private static float alpha = 0.5f; // 水印文字大小 public static final int FONT_SIZE = 18; // 水印文字字体 private static Font font = new Font("宋体", Font.PLAIN, FONT_SIZE); // 水印文字颜色 private static Color color = Color.red; // 水印之间的间隔 private static final int XMOVE = 80; // 水印之间的间隔 private static final int YMOVE = 80; /** * 给图片添加水印文字 * * @param logoText 水印文字 * @param srcImgPath 源图片路径 * @param targerPath 目标图片路径 */ public static void ImageByText(String logoText, String srcImgPath, String targerPath) { ImageByText(logoText, srcImgPath, targerPath, null); } /** * 获取文本长度。汉字为1:1，英文和数字为2:1 */ private static int getTextLength(String text) { int length = text.length(); for (int i = 0; i < text.length(); i++) { String s = String.valueOf(text.charAt(i)); if (s.getBytes().length > 1) { length++; } } length = length % 2 == 0 ? length / 2 : length / 2 + 1; return length; } /** * 给图片添加水印文字、可设置水印文字的旋转角度 * * @param logoText * @param srcImgPath * @param targerPath * @param degree */ public static void ImageByText(String logoText, String srcImgPath, String targerPath, Integer degree) { InputStream is = null; OutputStream os = null; try { // 源图片 Image srcImg = ImageIO.read(new File(srcImgPath)); int width = srcImg.getWidth(null);// 原图宽度 int height = srcImg.getHeight(null);// 原图高度 BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null), srcImg.getHeight(null), BufferedImage.TYPE_INT_RGB); // 得到画笔对象 Graphics2D g = buffImg.createGraphics(); // 设置对线段的锯齿状边缘处理 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(srcImg.getScaledInstance(srcImg.getWidth(null), srcImg.getHeight(null), Image.SCALE_SMOOTH), 0, 0, null); // 设置水印旋转 if (null != degree) { g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2); } // 设置水印文字颜色 g.setColor(color); // 设置水印文字Font g.setFont(font); // 设置水印文字透明度 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha)); int x = -width / 2; int y = -height / 2; int markWidth = FONT_SIZE * getTextLength(logoText);// 字体长度 int markHeight = FONT_SIZE;// 字体高度 // 循环添加水印 while (x < width * 1.5) { y = -height / 2; while (y < height * 1.5) { g.drawString(logoText, x, y); y += markHeight + YMOVE; } x += markWidth + XMOVE; } // 释放资源 g.dispose(); // 生成图片 os = new FileOutputStream(targerPath); ImageIO.write(buffImg, "JPG", os); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != is) is.close(); } catch (Exception e) { e.printStackTrace(); } try { if (null != os) os.close(); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { String srcImgPath = "C:\\Users\\31833\\Pictures\\82260302ed2143268990ab09b88207fb.png"; // 水印文字 String logoText = "传游防骗手册 QQ52xx05255"; String targerTextPath2 = "C:\\Users\\31833\\Pictures\\garget.png"; System.out.println("给图片添加水印文字开始..."); // 给图片添加斜水印文字 ImageByText(logoText, srcImgPath, targerTextPath2, 40); System.out.println("给图片添加水印文字结束..."); } } 2、指定位置水印 net.coobird thumbnailator 0.4.8 Java代码//file1为原图（File对象） File outputFile = new File(root + "/" + fileName + "-watermark" + suffix); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(FileUtil.readBytes(file1)); //要添加的水印图片 File waterMarkFile = new File(root + StringUtils.SLASH + "watermark.png"); //计算水印宽高 BufferedImage image = ImgUtil.read(file1); int height = image.getHeight() / 3; int width = image.getWidth() / 3; float opacity = 0.3f; Thumbnails.of(byteArrayInputStream) .watermark(Positions.BOTTOM_RIGHT, Thumbnails.of(waterMarkFile).size(width, height).asBufferedImage(), opacity) .watermark(Positions.BOTTOM_LEFT, Thumbnails.of(waterMarkFile).size(width, height).asBufferedImage(), opacity) .watermark(Positions.BOTTOM_CENTER, Thumbnails.of(waterMarkFile).size(width, height).asBufferedImage(), opacity) .watermark(Positions.TOP_RIGHT, Thumbnails.of(waterMarkFile).size(width, height).asBufferedImage(), opacity) .watermark(Positions.TOP_LEFT, Thumbnails.of(waterMarkFile).size(width, height).asBufferedImage(), opacity) .watermark(Positions.TOP_CENTER, Thumbnails.of(waterMarkFile).size(width, height).asBufferedImage(), opacity) .scale(1.0) .outputQuality(1) // 输出质量 .toFile(outputFile); // 输出文件
MongoDB百度百科：MongoDB是一个介于关系数据库和非关系数据库之间的产品，是非关系数据库当中功能最丰富，最像关系数据库的。它支持的数据结构非常松散，是类似json的bson格式，因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大，其语法有点类似于面向对象的查询语言，几乎可以实现类似关系数据库单表查询的绝大部分功能，而且还支持对数据建立索引。MongoDB是一种NoSQL数据库管理系统，它采用文档导向的数据模型，而不是传统的关系型数据库模型。介绍数据模型： MongoDB采用BSON（Binary JSON）文档存储数据，这些文档以键值对的形式组织在集合（Collection）中。文档可以包含嵌套的结构，使其更灵活。灵活性： 由于MongoDB采用文档导向的数据模型，数据的结构可以非常灵活，可以轻松地适应不断变化的业务需求。横向扩展性： MongoDB支持横向扩展，可以通过添加更多的节点来增加系统的处理能力，而无需修改应用程序代码。丰富的查询语言： MongoDB提供强大的查询语言，支持丰富的查询操作，包括范围查询、正则表达式匹配、排序等。索引支持： MongoDB支持各种类型的索引，可以提高查询性能。自动分片： MongoDB可以自动分片数据，支持水平扩展，使其适用于处理大规模数据。优点灵活性和模式自由： 可以轻松地处理不同结构和类型的数据。横向扩展： 可以通过添加更多的节点来实现横向扩展，提高性能和容量。高性能： MongoDB使用内存映射存储引擎，可以提供高性能的读写操作。JSON风格的文档： 数据以JSON风格的BSON文档存储，易于理解和使用。自动分片和负载均衡： MongoDB自动管理数据的分片和负载均衡，简化了扩展过程。社区支持： MongoDB有一个活跃的社区，提供丰富的文档和资源，便于开发者学习和解决问题。缺点事务支持的限制： 在某些版本中，MongoDB的事务支持相对较弱，不如关系型数据库那么强大。内存占用较大： MongoDB的内存占用相对较大，因为它使用内存映射文件来加速读写操作。不适合复杂的事务处理： 对于需要复杂事务处理的应用，MongoDB可能不是最佳选择。使用场景大数据量、高并发读写： 由于MongoDB的横向扩展能力和高性能，适用于处理大规模数据和高并发读写的场景，如日志处理、物联网数据等。需要灵活的数据模型： 对于数据结构经常变化、需要频繁调整的应用，MongoDB的灵活数据模型非常适合。实时分析和查询： 适用于需要实时分析和查询大量非结构化数据的场景，例如实时报表生成、实时监控等。文档存储需求： 对于需要存储和处理复杂文档结构的应用，MongoDB的文档导向模型非常方便。敏捷开发： 在敏捷开发环境中，由于MongoDB的模式自由性，可以更快地迭代和调整数据模型。体系机构数据类型MongoDB的最小存储单位就是文档document对象。文档document对象对应于关系型数据库的行。数据在MongoDB中以BSON（Binary-JSON）文档的格式存储在磁盘上。BSON（Binary Serialized Document Format）是一种类json的一种二进制形式的存储格式，简称 Binary JSON；BSON和JSON一样，支持内嵌的文档对象和数组对象，但是BSON有JSON没有的一些数据类型，如Date和Bin Data类型。BSON采用了类似于C语言结构体的名称、对表示方法，支持内嵌的文档对象和数组对象，具有轻量性、可遍历性、高效性的三个特点，可以有效描述非结构化数据和结构化数据。这种格式的优点是灵活性高，但它的缺点是空间利用率不是很理想。BSON中，除了基本JSON类型： string，integer，boolean，double，null，array和object，mongo还使用了特殊的数据类型。这些类型包括 date， object id， binary data， regular expression和code。每一个驱动都以特定语言的方式实现了这些类型，查看你的驱动的文档来获取详细信息1、安装笔者本次采用ZIP压缩包的格式安装传送门解压（将压缩包解压到全英文的文件夹下）解压后目录结构如下创建文件夹（data、logs）data为数据存储文件夹logs为日志存储文件夹在bin目录创建配置文件（mongod.cfg），配置文件如下# mongod.conf # for documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options/ # Where and how to store data. storage: # 数据存储路径 dbPath: D:\env\mongodb-win32-x86_64-windows-4.4.26\data journal: enabled: true # engine: # mmapv1: # wiredTiger: # where to write logging data. systemLog: destination: file logAppend: true # 日志存储文件路径 path: D:\env\mongodb-win32-x86_64-windows-4.4.26\logs\mongod.log # network interfaces net: # 连接端口 port: 27017 # 允许外网连接 bindIp: 0.0.0.0配置文件中的dbPath、path改成步骤二对应的路径打开命令提示符定位到bin目录输入命令安装mongod --install --config ${步骤3创建的配置文件路径}示例：mongod --install --config D:envmongodb-win32-x86_64-windows-4.4.26binmongod.cfg启动MongoDB服务：net start mongodb测试使用Navicat连接MongoDB数据库2、MongoDB常用命令提示：可将bin目录配置到Windows环境变量中，以便在任何路径都能使用mongo命令连接到数据库：mongo显示数据库列表：show dbs切换到指定数据库：use <database_name>显示当前数据库：db插入文档：db.<collection_name>.insert({ key: "value" })查询所有文档：db.<collection_name>.find()根据条件查询文档：db.<collection_name>.find({ key: "value" })更新文档：db.<collection_name>.update({ key: "value" }, { $set: { new_key: "new_value" } })删除文档：db.<collection_name>.remove({ key: "value" })创建集合：db.createCollection("<collection_name>")显示集合列表：show collections删除集合：db.<collection_name>.drop()创建索引：db.<collection_name>.createIndex({ key: 1 })显示索引列表：db.<collection_name>.getIndexes()删除索引：db.<collection_name>.dropIndex({ key: 1 })显示服务器状态：db.serverStatus()显示数据库状态：db.stats()退出MongoDB客户端：exit3、SpringBoot集成MongoDB加入 spring-boot-starter-data-mongodb 依赖 org.springframework.boot spring-boot-starter-data-mongodb org.springframework.boot spring-boot-starter-test org.projectlombok lombok application.ymlspring: data: mongodb: host: localhost port: 27017 database: testpackage com.pqkj.mongodb.doc; import lombok.Builder; import lombok.Data; import org.springframework.data.mongodb.core.mapping.Document; import java.util.List; /** * @author THF * @date 2023/12/26 11:34 */ @Data @Builder public class Student { /** * 姓名 */ private String name; /** * 年龄 */ private Integer age; /** * 性别 */ private String sex; /** * 爱好 */ private List<String> hobby; /** * 详细信息 */ private StudentDetail studentDetail; } package com.pqkj.mongodb.doc; import lombok.Builder; import lombok.Data; /** * @author THF * @date 2023/12/26 11:39 */ @Data @Builder public class StudentDetail { /** * 地址 */ private String address; /** * 电话 */ private String phone; } 测试类package com.pqkj.mongodb; import com.pqkj.mongodb.doc.Student; import com.pqkj.mongodb.doc.StudentDetail; 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 org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import java.util.List; /** * MongoTemplate 测试 CURD * @author THF * @date 2023/12/26 11:29 */ @Slf4j @SpringBootTest public class MongoDBTest { @Autowired private MongoTemplate mongoTemplate; /** * 新增文档 */ @Test void insert() { List<Student> studentList = List.of( Student.builder() .name("小唐") .age(18) .sex("男") .hobby(List.of("羽毛球", "游戏", "Code")).build(), Student.builder() .name("小陈") .age(18) .sex("女") .hobby(List.of("Lion Mountain Code")) .studentDetail(StudentDetail.builder() .address("众创大厦") .phone("110").build()).build() ); for (Student student : studentList) { mongoTemplate.insert(student); } } /** * 查询所有 */ @Test void selectAll(){ List<Student> studentList = mongoTemplate.findAll(Student.class); studentList.forEach((student -> log.info(student.toString()))); } /** * 模糊查询 */ @Test void likeSelect(){ String name="陈"; String regex=String.format("%s%s%s","^.*",name,".*$"); List<Student> studentList = mongoTemplate.find(Query.query( Criteria.where("name").regex(regex) ),Student.class); studentList.forEach((student -> log.info(student.toString()))); } /** * 根据条件删除 */ @Test void remove(){ mongoTemplate.remove(Query.query( Criteria.where("name").in(List.of("小陈")) ),Student.class); } }