Java实现自动化pdf打水印小项目 使用技术pdfbox、Documents4j

文章目录

  • 前言
  • 源码获取
  • 一、需求说明
  • 二、 调研
    • pdf处理工具
    • word处理工具
  • 三、技术栈选择
  • 四、功能实现
    • 实现效果
    • 详细功能介绍
    • 详细代码实现
      • 项目目录
      • WordUtils
      • Main类实现部分:第一部分
      • Main类实现部分:第二部分
      • Main类实现部分:第三部分
  • 资料获取

前言

博主介绍:✌目前全网粉丝2W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。

涵盖技术内容:Java后端、算法、分布式微服务、中间件、前端、运维、ROS等。

博主所有博客文件目录索引:博客目录索引(持续更新)

视频平台:b站-Coder长路


源码获取

项目源码:Gitee、Github

本篇文档的视频系列讲解:Java实现自动化pdf打水印工具 开源PDF工具PDFBoxWord、Word转PDF开源工具Documents4j


一、需求说明

背景:做默默学代理需要去给每日日日练打上水印,对于重复性操作pdf编辑统一通过程序来批量处理。

目标:实现一个自定义的pdf水印工具。

第一部分:word转pdf

第二部分:pdf编辑

页眉:咨询专转本默默学课程联系官方报名处QQ:3503851091,更多资料可加群828303961
页脚:江苏专转本公众号:专转本智慧树
水印:江苏专转本网课报名vx:mmxchanglu
	属性:旋转45°,不透明度30%
右上角图片:专转本咨询群二维码.jpg
	属性:缩小比例30%

二、 调研

pdf处理工具

开源项目-pdfbox:

  • 官方网站:https://pdfbox.apache.org/

x-easypdf:国内开发,基于pdfbox。

Stirling-PDF:一款优秀的开源PDF处理工具:Stirling-PDF(Github)

  • 引用开源工具:org.apache.pdfbox

ttf文件:

  • 阿里巴巴普惠体:https://www.iconfont.cn/fonts/

word处理工具

word相关开源工具如下:

  1. Apache POI:
    • Apache POI 是一个 Java 库,可以用于处理 Microsoft Office 文档,包括 Word 文档。您可以使用 Apache POI 来读取 Word 文档并将其转换为 PDF。需要注意的是,Apache POI 主要用于读取和编辑 Word 文档,转换为 PDF 需要额外的步骤。
  2. iText:
    • iText 是一个流行的 Java 库,用于处理 PDF 文档。它可以用于将 Word 文档转换为 PDF。请注意,iText 并不是完全开源的,它有一些限制,具体取决于您的使用情况。
  3. LibreOffice:
    • LibreOffice 是一个开源办公套件,包括一个强大的文档编辑器。您可以使用 LibreOffice 的命令行接口(soffice)来将 Word 文档转换为 PDF。LibreOffice 在许多平台上都可用,并且支持多种文档格式的转换。
  4. Pandoc:
    • Pandoc 是一个通用文档转换工具,支持多种输入和输出格式。它可以将 Word 文档转换为 PDF,同时还支持许多其他文档格式。Pandoc 使用命令行进行操作。

实际选择:Documents4j

	Documents4j 是一个开源的 Java 库,用于在 Java 应用程序中进行 Microsoft Office 文档(如 Word、Excel、PowerPoint 等)的转换。它利用 Microsoft Office 的本机 API,通过启动 Microsoft Office 进程来执行文档转换。Documents4j 提供了一种简单的方式来将 Office 文档转换为其他格式,例如将 Word 文档转换为 PDF、将 Excel 表格转换为 CSV 等。
	Documents4j 的工作原理是通过将 Microsoft Office 作为外部进程启动,并与其进行通信来执行文档转换任务。这种方法使得可以利用 Microsoft Office 的强大功能来执行文档转换,同时又能够在 Java 环境中方便地进行集成和控制。
	Documents4j 提供了一个简单的 API,使得在 Java 应用程序中执行文档转换变得简单。它支持多线程操作,并且具有一定的性能优化,使得可以高效地处理大量文档转换任务。

相关实际案例:

  1. 不要在去充VIP啦 ,Java 实现 PDF、Word 互转:公众号 WeDayDayUp ,发送 案例 即可,gitee地址,https://gitee.com/HelloWangXianLin/xiaoxiao-demo

三、技术栈选择

语言选择:Java

pdf水印、编辑开源工具:pdfbox

word转pdf:Documents4j


四、功能实现

实现效果

实现功能效果:最终得到的是已经打上广告以及水印的pdf文件

image-20240215121714140

代码运行效果:

image-20240215121533848


详细功能介绍

实现思路如下

1、给定一个目录,检索该目录下所有文件,筛选出所有的docx以及PDF文件。

2、将目录下docx文件转为pdf文件,并将docx文件统一剪切到tmp目录。

3、对所有的pdf文件来进行最定义【水印+广告】处理,将所有水印处理过的pdf文件统一输出到pdf目录。【其中文件名包含有如:高数,最终输出的名字转为 2024.2.15高数日日练及答案】

页眉:咨询专转本默默学课程联系官方报名处QQ:3503851091,更多资料可加群828303961
页脚:江苏专转本公众号:专转本智慧树
水印:江苏专转本网课报名vx:mmxchanglu
	属性:旋转45°,不透明度30%
右上角图片:专转本咨询群二维码.jpg
	属性:缩小比例30%

pdf中水印增加的效果:主要就是这四个部分

image-20240215121938960


详细代码实现

项目目录

image-20240215120327526

WordUtils:word转pdf工具类
Main:任务类
images/:水印素材图片
ttfs/:ttf字体

WordUtils

package com.changlu.utils;

import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import java.io.*;

public class WordUtils {

    /**
     * Word转PDF
     * @param filePath 源docx文件目录及名称  示例:C:\Users\93997\Desktop\watermark tools\watermarkTools\src\main\resources\2024-2-8计算机.docx
     * @param outFilePath 输出文件目录及名称 示例:C:\Users\93997\Desktop\watermark tools\watermarkTools\src\main\resources\2024-2-8.pdf
     */
    public static void wordToPdf(String filePath, String outFilePath) {
        //源文件地址
        File inputWord = new File(filePath);
        //导出文件地址
        File outputFile = new File(outFilePath);
        InputStream doc = null;
        OutputStream outputStream = null;
        try {
            doc = new FileInputStream(inputWord);
            outputStream = new FileOutputStream(outputFile);
            IConverter converter = LocalConverter.builder().build();
            //转换docx=>pdf
            boolean flag = converter.convert(doc).as(DocumentType.DOC).to(outputStream).as(DocumentType.PDF).execute();
            if (flag) {
                converter.shutDown();
            }
            doc.close();
            outputStream.close();
            System.out.println("文件名:" + outFilePath + " 转换成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        String filePath = "C:\\Users\\93997\\Desktop\\watermark tools\\watermarkTools\\src\\main\\resources\\2024-2-8计算机.docx";
        String outFilePath = "C:\\Users\\93997\\Desktop\\watermark tools\\watermarkTools\\src\\main\\resources\\2024-2-8.pdf";
        //word转pdf
        WordUtils.wordToPdf(filePath, outFilePath);
    }

}

Main类实现部分:第一部分

功能描述:给定一个目录,检索该目录下所有文件,筛选出所有的docx以及PDF文件。

package com.changlu;

import com.changlu.utils.WordUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description:
 * @Author: changlu
 * @Date: 11:19 AM
 */
public class Main {

    //处理文件目录
    private static String directoryPath = "C:\\Users\\93997\\Desktop\\日日练";

    public static void main(String[] args) {
        //任务1:给定一个目录,检索该目录下所有文件,筛选出所有的docx以及PDF文件。
        File directory = new File(directoryPath);
        //搜集所有的docx文档
        List<String> waitWorkDocs = new ArrayList<>();
        //搜集所有的pdf名称
        List<String> waitWorkPDFs = new ArrayList<>();
        //遍历目录下所有的文件
        for (File file : directory.listFiles()) {
            //筛选不是目录
            if (!file.isDirectory()) {
                if (file.getName().endsWith(".docx")) {
                    //添加docx的文件名
                    waitWorkDocs.add(file.getName());
                }else if (file.getName().endsWith(".pdf")) {
                    waitWorkPDFs.add(file.getName());
                }
            }
        }   
    }
}

Main类实现部分:第二部分

功能描述:将目录下docx文件转为pdf文件,并将docx文件统一剪切到tmp目录。

/**
 * @Description:
 * @Author: changlu
 * @Date: 11:19 AM
 */
public class Main {

    //处理文件目录
    private static String directoryPath = "C:\\Users\\93997\\Desktop\\日日练";
    private static String docxDirectoryPath = "tmp";//docs临时存放目录

    public static void main(String[] args) {
        //任务1:xxx
        
        //任务2:将目录下docx文件转为pdf文件,并将docx文件统一剪切到tmp目录。
        System.out.println("开始执行word转pdf任务...");
        //临时存放docx文件的目录 tmp,若是不存在创建
        String targetTmpDirectory = directoryPath + File.separator + docxDirectoryPath;
        if (!Files.exists(Paths.get(targetTmpDirectory))) {
            new File(targetTmpDirectory).mkdirs();
        }
        //将所有的docx进行转换,word转pdf
        for (String docxName : waitWorkDocs) {
            String originPath = directoryPath + File.separator + docxName;
            String targetPath = originPath.replace(".docx", ".pdf");
            //docx转为pdf文件
            WordUtils.wordToPdf(originPath, targetPath);
            //将docx剪切到tmp文件
            String targetTmpPath = targetTmpDirectory + File.separator + docxName;
            try {
                Files.move(Paths.get(originPath), Paths.get(targetTmpPath));
            } catch (IOException e) {
                e.printStackTrace();
            }
            //添加pdf名称到pdf集合中
            String pdfName = targetPath.substring(targetPath.lastIndexOf(File.separator) + 1);
            waitWorkPDFs.add(pdfName);
        }
    }
}

Main类实现部分:第三部分

/**
 * @Description:
 * @Author: changlu
 * @Date: 11:19 AM
 */
public class Main {

    //处理文件目录
    private static String directoryPath = "C:\\Users\\93997\\Desktop\\日日练";
    private static String docxDirectoryPath = "tmp";//docs临时存放目录

    public static void main(String[] args) {
        //任务1:xxx
        
        //任务2:xxx
        
		//任务3:对所有的pdf文件来进行最定义【水印+广告】处理,将所有水印处理过的pdf文件统一输出到pdf目录。
        System.out.println();
        System.out.println("开始处理pdf打水印、广告任务....");
        //临时存放docx文件的目录 tmp,若是不存在创建
        String targetPDFDirectory = directoryPath + File.separator + pdfDirectoryPath;
        if (!Files.exists(Paths.get(targetPDFDirectory))) {
            new File(targetPDFDirectory).mkdirs();
        }
        //遍历所有的pdf文件来去添加水印及其他任务并输出pdf
        for (String waitWorkPDFName : waitWorkPDFs) {
            String originPDFPath = directoryPath + File.separator + waitWorkPDFName;
            String targetPDFPath = targetPDFDirectory + File.separator + fileNameTransfer(waitWorkPDFName);
            //执行真正的水印+广告任务
            try {
                work(originPDFPath, targetPDFPath);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    //根据当前文件名称来匹配相应的文件名称
    public static String fileNameTransfer(String originName) {
        for (Map.Entry<String, String> entry : subjectNamesMap.entrySet()) {
            String name = entry.getKey();
            //若是当前名称中包含有科目名称
            if (originName.contains(name)) {
                return entry.getValue();
            }
        }
        return "未知" + System.currentTimeMillis() + ".pdf";
    }

    /**
     * 实际水印处理任务
     */
    public static void work(String originPDFPath, String targetPDFPath) throws Exception{
        //读取resources目录下input.pdf文件
//        InputStream is = Main.class.getClassLoader().getResourceAsStream("input.pdf");
        InputStream is = new FileInputStream(originPDFPath);
        PDDocument pdDocument = PDDocument.load(is);

        //自定义字体 C:\Users\93997\Desktop\watermark tools\watermarkTools\target\classes\ttfs
        //URLDecoder.decode() 方法来解码 URL 编码的路径,将 %20 转换回空格
//        PDType0Font font = PDType0Font.load(pdDocument, new File("C:\\Users\\93997\\Desktop\\watermark tools\\watermarkTools\\src\\main\\resources\\ttfs\\Alibaba_PuHuiTi_2.0_65_Medium_65_Medium.ttf"));
        String fontFile = URLDecoder.decode(Main.class.getClassLoader().getResource(File.separator + "ttfs" + File.separator + "Alibaba_PuHuiTi_2.0_65_Medium_65_Medium.ttf").getFile(), "UTF-8");
        PDType0Font font = PDType0Font.load(pdDocument, new File(fontFile));
        float fontSize = 10; // 设置字体大小为12

        // 设置透明度状态对象
        PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
        graphicsState.setNonStrokingAlphaConstant(0.2f);
        graphicsState.setAlphaSourceFlag(true);
        graphicsState.setStrokingAlphaConstant(0.2f);

        //设置新的页眉
        String headerText = "咨询专转本默默学课程联系官方报名处QQ:3503851091,更多资料可加群828303961";
        String footerText = "江苏专转本公众号:专转本智慧树";
        String waterText = "江苏专转本网课报名vx:mmxchanglu";

        //遍历原先的pdf文档
        for (PDPage page : pdDocument.getPages()) {
            float pageWidth = page.getMediaBox().getWidth();
            float pageHeight = page.getMediaBox().getHeight();
            //计算页眉的居中位置
            float headerTextWidth = font.getStringWidth(headerText) / 1000 * fontSize;
            float headerCenteredX = (pageWidth - headerTextWidth) / 2; // 计算水平居中位置
            //计算页脚的居中位置
            float footerTextWidth = font.getStringWidth(footerText) / 1000 * fontSize;
            float footerCenteredX = (pageWidth - footerTextWidth) / 2; // 计算水平居中位置

            // 创建用于页眉的内容流
            PDPageContentStream headerContentStream = new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true, true);
            headerContentStream.beginText(); // 开始文本操作
            headerContentStream.setFont(font, fontSize); // 设置字体和字号
            headerContentStream.newLineAtOffset(headerCenteredX, page.getMediaBox().getHeight() - 30); // 设置文本起始位置
            headerContentStream.showText(headerText); // 绘制页眉内容
            headerContentStream.endText(); // 结束文本操作
            headerContentStream.close(); // 关闭内容流

            // 添加页脚
            PDPageContentStream footerContentStream = new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true, true);
            footerContentStream.beginText(); // 开始文本操作
            footerContentStream.setFont(font, fontSize); // 设置字体和字号
            footerContentStream.newLineAtOffset(footerCenteredX, 30); // 设置文本起始位置
            footerContentStream.showText(footerText); // 绘制页脚内容
            footerContentStream.endText(); // 结束文本操作
            footerContentStream.close(); // 关闭内容流

            //添加水印   要求:旋转45°,不透明度30%
            float waterTextWidth = font.getStringWidth(waterText) / 1000 * 30;
            float waterCenteredX = (pageWidth - waterTextWidth) / 2;
            float waterCenteredY = pageHeight / 2;
            //创建一个水印内容流
            PDPageContentStream waterContentStream = new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true, true);
            waterContentStream.beginText();
            waterContentStream.setFont(font, 30);
            // 设置不透明度
            waterContentStream.setNonStrokingColor(0, 0, 0); // black color
            waterContentStream.setStrokingColor(0, 0, 0); // black color
            waterContentStream.setGraphicsStateParameters(graphicsState);//设置透明度
            //设置旋转文本 45° 对于tx、ty是以左下角为偏移位置中心来进行旋转角度
            waterContentStream.setTextRotation(Math.toRadians(45), 400, -50);
            //设置文本
            waterContentStream.newLineAtOffset(waterCenteredX, waterCenteredY);
            waterContentStream.showText(waterText);
            waterContentStream.endText();
            waterContentStream.close();

            //添加图片水印
            //创建一个水印内容流
            PDPageContentStream imageContentStream = new PDPageContentStream(pdDocument, page, PDPageContentStream.AppendMode.APPEND, true, true);
            // 创建图像对象
//            PDImageXObject image = PDImageXObject.createFromFile("C:\\Users\\93997\\Desktop\\watermark tools\\watermarkTools\\src\\main\\resources\\images\\ConsultationGroupQRCode.jpg", pdDocument);
            String pictureFile = URLDecoder.decode(Main.class.getClassLoader().getResource(File.separator + "images" + File.separator + "ConsultationGroupQRCode.jpg").getFile(), "UTF-8");
            PDImageXObject image = PDImageXObject.createFromFile(pictureFile, pdDocument);
            // 计算图像的宽度和高度(缩小比例为0.3)
            float imageWidth = (float) (image.getWidth() * 0.25);
            float imageHeight = (float) (image.getHeight() * 0.25);
            //具体图片位置
            float imageX = pageWidth - imageWidth - 10;
            float imageY = pageHeight - imageHeight - 10;

            // 在指定位置绘制图像
            imageContentStream.drawImage(image, imageX, imageY, imageWidth, imageHeight);
            imageContentStream.close();
        }

        //目标目录
//        File outputFile = new File("C:\\Users\\93997\\Desktop\\watermark tools\\watermarkTools\\src\\main\\resources\\output.pdf");
        File outputFile = new File(targetPDFPath);
        // 若是文件存在先进行删除
        Files.deleteIfExists(Paths.get(outputFile.toURI()));
        // 保存修改后的文档
        pdDocument.save(outputFile);
        System.out.println("转换任务:" + originPDFPath + "=>" + targetPDFPath + " 成功!");
        // 关闭文档
        pdDocument.close(); // 关闭文档
    }
}

资料获取

大家点赞、收藏、关注、评论啦~

精彩专栏推荐订阅:在下方专栏👇🏻

  • 长路-文章目录汇总(算法、后端Java、前端、运维技术导航):博主所有博客导航索引汇总
  • 开源项目Studio-Vue—校园工作室管理系统(含前后台,SpringBoot+Vue):博主个人独立项目,包含详细部署上线视频,已开源
  • 学习与生活-专栏:可以了解博主的学习历程
  • 算法专栏:算法收录

更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅


整理者:长路 时间:2024.2.16

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/397009.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C#】使用代码实现龙年春晚扑克牌魔术(守岁共此时),代码实现篇

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握。…

Sora:新一代实时音视频通信框架

一、Sora简介 Sora是一个开源的实时音视频通信框架&#xff0c;旨在提供高效、稳定、可扩展的音视频通信解决方案。它基于WebRTC技术&#xff0c;支持跨平台、跨浏览器的实时音视频通信&#xff0c;并且具备低延迟、高并发、易集成等特点。 --点击进入Sora(一定要科学哦&#x…

爬虫学习笔记-scrapy爬取电影天堂(双层网址嵌套)

1.终端运行scrapy startproject movie,创建项目 2.接口查找 3.终端cd到spiders,cd scrapy_carhome/scrapy_movie/spiders,运行 scrapy genspider mv https://dy2018.com/ 4.打开mv,编写代码,爬取电影名和网址 5.用爬取的网址请求,使用meta属性传递name ,callback调用自定义的…

PyQt应用程序中的多线程:使用Qt还是Python线程?

多线程模块能够更加高效得完成任务&#xff0c;但是在PyQt 应用程序中实现多线程可以使用 Qt 的线程模块&#xff08;QThread&#xff09;或者 Python 的 threading 模块。两者各有优劣&#xff0c;具体选择取决于项目需求和个人偏好。下面我们将以案例来说明两种模块具体得优缺…

欠定方程组及其求解

欠定方程组是指方程的数量少于未知数的数量的方程组。在这种情况下&#xff0c;通常有无限多个解&#xff0c;因为给定的方程不足以唯一确定所有未知数的值。在某些情况下&#xff0c;我们可以利用额外的信息或假设&#xff0c;如稀疏性或其他约束&#xff0c;来找到一个合理的…

嵌入式 系统 开发 - 第一件事 “搭开发环境”

无论是对DSP&#xff0c;FPGA&#xff0c;或其他可编程芯片开发 都要 “搭开发环境” &#xff1a; 懒得写太多字&#xff0c;画个图来扯淡吧&#xff01; 看看实际 怎么搞的 &#xff1a;&#xff09; 这张照片仅仅是 老哥 自己的一个DSP开发实际连结的搞法儿啊&#xff0c…

【Docker】集群容器监控和统计 Portainer基本用法

Portainer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用川于方便地管理Docker环境&#xff0c;包括单机环境和集群环境。 主要功能&#xff1a;实现集群容器的监控和统计 下载安装 官网&#xff1a;https://www.portainer.io 文档&#xff1a;https://do…

移动通信相关知识学习笔记

一、移动通信架构简图 移动无线的接入网是专指各种基站设备。核心网就是各种交换机。 二、无线信号基本原理 无线网络中&#xff0c;使用AP设备和天线来实现有线和无线信号互相转换。如上图所示&#xff0c;有线网络侧的数据从AP设备的有线接口进入AP后&#xff0c;经AP处理为…

ElasticSearch之Index Template 和Dynamic Template

写在前面 在ElasticSearch之Mapping 一文中我们一起看了es的dynamic mapping机制&#xff0c;通过该机制允许我们不需要显式的定义mapping信息&#xff0c;而是es根据插入的文档值来自动生成 &#xff0c;比如插入如下的文档&#xff1a; {"firstName": "Chan…

MySQL Replication

0 序言 MySQL Replication 是 MySQL 中的一个功能&#xff0c;允许从一个 MySQL 数据库服务器&#xff08;称为主服务器或 master&#xff09;复制数据和数据库结构到另一个服务器&#xff08;称为从服务器或 slave&#xff09;。这种复制是异步的&#xff0c;意味着从服务器不…

cRIO9040中NI9381模块的测试

硬件准备 CompactRIO9040NI9381直流电源&#xff08;可调&#xff09;网线 软件安装 下载地址 LabVIEW Real-Time 模块 NI CompactRIO 设备驱动程序 LabVIEW FPGA 模块&#xff08;可选&#xff09; 仅以下情况需要LabVIEW FPGA模块&#xff1a; 想为CompactRIO终端设计FP…

数据结构之线性表插入与删除运算

线性表 线性表的定义 线性表&#xff0c;或称表&#xff0c;是一种非常灵便的结构&#xff0c;可以根据需要改变表的长度&#xff0c;也可以在表中任何位置对元素进行访问、插入或删除等操作。另外&#xff0c;还可以将多个表连接成一个表&#xff0c;或把一个表拆分成多个表…

【git 使用】超级好用的 git reset 和 git revert 功能对比和使用方法

首先你要知道 git 区分暂存区和工作区&#xff0c;如果你用过 sourcetree 你就会知道 git reset 超级好用 git reset 命令用于将当前分支的 HEAD 指针移动到指定的提交&#xff0c;并且可以选择性地修改工作区和暂存区的状态。git reset 命令有几种常用的用法&#xff0c;主要…

【conda环境 安装 tensorflow2.2】 解决方案

1.检查anaconda安装&#xff1a;在cmd输入 conda --version 2.检测已经安装的环境&#xff1a;conda info --envs 3.新建一个python3.5的环境&#xff0c;tensorflow&#xff1a; ###conda create -n xxx python3.5 xxx为虚拟环境名 ###conda create -n xxx python3.6 xxx为虚拟…

VO、DTO、DO、BO、PO

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 VO、DTO、DO、BO1.概念阿里Java开发手册分层领域模型&#xff1a; 2. VO 和 DTO 使用场景以下是一个使用VO和DTO的典型案例&#xff1a; 3.BO和DTO的区别 案例 VO、…

【SpringBoot3】Spring Security 常用注解

注&#xff1a;本文基于Spring Boot 3.2.1 以及 Spring Security 6.2.1 Spring Security 6 的常用注解包括以下几种&#xff0c;通过这些注解可以更加方便的控制资源权限。 Secured &#xff1a;方法执行前检查&#xff0c;直接判断有没有对应的角色PreAuthorize&#xff1a;方…

Qt C++春晚刘谦魔术约瑟夫环问题的模拟程序

什么是约瑟夫环问题&#xff1f; 约瑟夫问题是个有名的问题&#xff1a;N个人围成一圈&#xff0c;从第一个开始报数&#xff0c;第M个将被杀掉&#xff0c;最后剩下一个&#xff0c;其余人都将被杀掉。例如N6&#xff0c;M5&#xff0c;被杀掉的顺序是&#xff1a;5&#xff…

tkinter做一个秒表

文章目录 需求和框架布局和主流程计时函数 需求和框架 本文试图实现一个简单的秒表&#xff0c;内容如下 这个软件非常简单&#xff0c;其UI元素只有一个文字标签外加三个按钮&#xff0c;这三个按钮的功能如下 点击Start按钮&#xff0c;开始进行计时&#xff0c;同时Start变…

已解决:IDEA中@Autowired自动注入MyBatis Mapper报红警告的几种解决方法

今天在使用 IDEA 使用 MyBatis 的时候遇到了这种情况&#xff1a; 可以看到 userMapper 下有个红色的波浪警告&#xff0c;虽然代码没有任何问题&#xff0c;能正常运行&#xff0c;但是这个红色警告在这里杵着确实让人很窝心。 于是我在网上找了找&#xff0c;最终明白了原因…

【鸿蒙系统学习笔记】状态管理

一、介绍 资料来自官网&#xff1a;文档中心 在声明式UI编程框架中&#xff0c;UI是程序状态的运行结果&#xff0c;用户构建了一个UI模型&#xff0c;其中应用的运行时的状态是参数。当参数改变时&#xff0c;UI作为返回结果&#xff0c;也将进行对应的改变。这些运行时的状…