《通俗易懂 · JSqlParser 解析和构造SQL》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗

🌻 希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 写在前面的话
    • 技术简介
      • 基础间接
      • 特色说明
      • 核心功能
      • 竞品分析
    • 技术扩展
      • 基础入门
      • 主要模块
      • 解析SQL能力
      • 构建SQL能力
    • 企业实战
      • 运用场景
      • 企业实战 - SQL 解析
      • 企业实战 - SQL 生成
    • 总结陈词

写在前面的话

博主公司在近期新产品的技术选型当中,涉及到SQL解析与生成等场景,这部分选择了JSqlParser,这里进行简单的介绍说明,大家可以按需选择。

参考:JSqlParser 官网


技术简介

基础间接

JSqlParser 是一个功能强大的 Java 库,专为解析、修改和生成 SQL 语句而设计。它不仅能够处理复杂的 SQL 语法,还提供了丰富的 API,使得开发者可以轻松地对 SQL 语句进行各种操作。JSqlParser 的主要目标是为开发人员提供一个可靠的工具,以便在应用程序中高效地管理和优化 SQL 语句。

JSqlParser 的设计理念是简单易用,同时具备高度的灵活性和扩展性。无论是初学者还是经验丰富的开发人员,都可以通过 JSqlParser 快速上手并解决实际问题。该库支持多种 SQL 方言,包括 MySQL、PostgreSQL、Oracle 和 SQL Server 等,这使得它在多数据库环境中具有广泛的应用前景。

特色说明

1、功能丰富

JSqlParser 提供了全面的 SQL 解析功能,可以解析几乎所有的 SQL 语句类型,包括但不限于 SELECT、INSERT、UPDATE、DELETE、CREATE TABLE 和 ALTER TABLE 等。此外,它还支持复杂的子查询、联合查询和嵌套查询等高级语法。通过 JSqlParser,开发人员可以轻松地将 SQL 语句转换为抽象语法树(AST),从而方便地进行进一步的操作和分析。

2、易于使用与扩展

JSqlParser 的 API 设计简洁明了,使得开发人员可以快速上手并集成到现有的项目中,解析或生成一个简单的 SQL 语句只需要几行代码。

3、高度可扩展

JSqlParser 不仅提供了丰富的内置功能,还允许开发人员根据需要进行扩展。例如,可以通过实现自定义的 Visitor 接口来遍历和修改抽象语法树。这种灵活性使得 JSqlParser 可以适应各种复杂的应用场景,如 SQL 语句的动态生成、查询优化和安全性检查等。

4、支持多种 SQL 方言

JSqlParser 支持多种主流的 SQL 方言,包括 MySQL、PostgreSQL、Oracle 和 SQL Server 等。这意味着开发人员可以在不同的数据库环境中使用同一个库,而无需担心兼容性问题。这种跨平台的支持使得 JSqlParser 成为了一个多数据库应用的理想选择。

核心功能

前面已经写了太多文字了,核心功能不展开了。

无非就是:SQL解析、SQL调整、SQL生成。

再简化一下:SQL解析和生成!

竞品分析

JSqlParser 同类型的工具,用于解析 SQL 语句,主要有以下几种:

  • **SQLparser: **一个轻量级的 Java 库,专注于 SQL 解析,支持多种 SQL 方言,功能相对 JSqlParser 较为精简。
  • **Apache Calcite: **一个用于 SQL 查询优化的框架,也包含 SQL 解析器, 更侧重于查询优化和执行,解析功能作为其一部分。
  • **ANTLR: ** 一个强大的语法分析工具,可以用于构建自定义的 SQL 解析器,灵活性高,但需要用户定义语法规则。
  • **SQL-Java: **一个用于解析和操作 SQL 语句的 Java 库,功能与 JSqlParser 类似,但可能在特定方面有不同侧重。

温馨提示:个人认为,这种工具类的,不用花费大量时间去纠结技术选型,能满足开发需求,用的顺手即可。


技术扩展

基础入门

Tips:截止编写日期,最新版本是5.0,需要使用JDK11以上版本。

Tips:针对后端框架没有限制,可以当作 Hutools 那样的工具类使用。

Step1、引入Maven依赖

<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>5.0</version>
</dependency>

Step2、编写SQL解析的测试方法

@Test
public void parseSqlTemp() throws JSQLParserException {
    String sql = "SELECT id,name FROM staff_member WHERE nickname= '刘'";
    Statement statement = CCJSqlParserUtil.parse(sql);
    if (statement instanceof Select selectStatement) {
        PlainSelect plainSelect = selectStatement.getPlainSelect();
        log.info("==> JsqlParser SQL: {}", selectStatement);
        log.info("==> FromItem: {}", plainSelect.getFromItem());
        log.info("==> SelectItem: {}", plainSelect.getSelectItems());
        log.info("==> Where: {}", plainSelect.getWhere());
        log.info("==> Joins: {}", plainSelect.getJoins());
    }
}

// 输出信息:
==> JsqlParser SQL: SELECT id, name FROM staff_member WHERE nickname = '刘'
==> FromItem: staff_member
==> SelectItem: [id, name]
==> Where: nickname = '刘'

Step3、编写SQL生成的测试方法

@Test
public void testSql1() {
    Table t1 = new Table("tab1").withAlias(new Alias("t1").withUseAs(true)); // 表1
    Table t2 = new Table("tab2").withAlias(new Alias("t2", false)); // 表2
    PlainSelect plainSelect = new PlainSelect().addSelectItems(new AllColumns())
            .withFromItem(t1); // SELECT * FROM tab1 AS t1
    Join join = new Join(); // 创建Join对象
    join.withRightItem(t2); // 添加Join的表 JOIN t2 =>JOIN tab2 t2
    EqualsTo equalsTo = new EqualsTo(); // 添加 = 条件表达式  t1.user_id  = t2.user_id
    equalsTo.setLeftExpression(new Column(t1, "user_id "));
    equalsTo.setRightExpression(new Column(t2, "user_id "));
    join.withOnExpression(equalsTo);// 添加ON
    plainSelect.addJoins(join);
    System.err.println(plainSelect); 
    // SELECT * FROM tab1 AS t1 JOIN tab2 t2 ON t1.user_id  = t2.user_id
}

主要模块

1. Expression

expression 包含了 SQL 中的各种表达式类,这些类用于构建和表示 SQL 语句中的条件和表达式。常见的表达式类包括:

EqualsTo:表示等于操作符(=)。

InExpression:表示 IN 操作符,用于检查某个值是否在一组值中。

BinaryExpression:表示二元操作符(如加法、减法等)。

Function:表示 SQL 函数调用。

Column:表示 SQL 中的列。

这些表达式类使得用户可以方便地构建复杂的 SQL 查询条件。

2. Schema

schema 模块主要用于表示数据库的结构,包括表、列、约束等。常见的类包括:

Table:表示数据库中的表。

Column:表示表中的列,通常包含列名、数据类型等信息。

Index:表示表上的索引。

ForeignKey:表示表之间的外键关系。

这些类帮助用户理解和操作数据库的结构,便于进行元数据的管理和查询。

3. Parser

parser 模块包含了 SQL 解析的相关类,主要负责将 SQL 字符串解析为 Java 对象。常见的类包括:

CCJSqlParserUtil:提供了静态方法来解析 SQL 语句,返回相应的语法树。

SQLParser:核心解析器,负责将 SQL 语句分解为不同的部分。

ExpressionParser:用于解析 SQL 中的表达式部分。

通过这些类,用户可以轻松地将 SQL 语句转换为可操作的 Java 对象。

4. Statement

statement 模块封装了各种数据库操作的对象,表示不同类型的 SQL 语句。常见的类包括:

Select:表示 SELECT 查询语句。

Insert:表示 INSERT 操作。

Update:表示 UPDATE 操作。

Delete:表示 DELETE 操作。

CreateTable:表示创建表的语句。

这些类使得用户可以方便地构建、修改和执行 SQL 语句。

解析SQL能力

JSqlParser可以解析SQL为Java对象,以便于获取SQL中的相关信息或可进行修改。

一般通过 CCJSqlParserUtil 和 CCJSqlParserManager 解析SQL。

CCJSqlParserUtil 适合于简单的情况,CCJSqlParserManager 则拥有更强大的功能。

【示例代码】

这边来一段示例代码,其实自己准备一个SQL,尝试一下很容易理解。

基本步骤:使用CCJSqlParserUtil解析sql,获得Statement,如果是查询就转换PlainSelect类型,然后调其相关方法获取更详细的内容。

@Test
public void testSelectAll() throws JSQLParserException {

    // 准备一段基础SQL
    String sql = """
        SELECT t.pres_no,
               t.patient_id,
               t.exec_dept_code,
               t1.patient_id,
               t1.patient_name,
               t1.patient_sex,
               (SELECT a.DEPT_NAME
                  FROM ZOEDICT.DIC_DEPT_DICT a
                 WHERE a.DEPT_CODE = t.exec_dept_code) AS "EXEC_DEPT_NAME"
          FROM ZOEPRES.PRES_OUTP_PRES_MASTER t
          JOIN zoepatient.pat_basic_info t1 ON t.patient_id = t1.patient_id
         WHERE t.patient_id = '5000022933'
           AND t.event_no = 'D3389411'
        """;

    // 利用 CCJSqlParserUtil 解析SQL
    Statement statementTemp = CCJSqlParserUtil.parse(sql);
    if (statementTemp instanceof Select selectStatement) {
        PlainSelect plainSelect = selectStatement.getPlainSelect();
        log.info("===> JsqlParser SQL: {}", selectStatement);
        log.info("===> FromItem: {}", plainSelect.getFromItem());
        log.info("===> SelectItem: {}", plainSelect.getSelectItems());
        log.info("===> Where: {}", plainSelect.getWhere());
        log.info("===> Join: {}", plainSelect.getJoins());
    }

    // 创建 TablesNamesFinder 实例,提取表名
    // 这里会连JOIN和子查询的表一起输出,总共三张
    TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
    List<String> tableList = tablesNamesFinder.getTableList(statementTemp);
    System.out.println(tableList);
}

【PlainSelect 方法列举】

获取和设置表(From子句):
FromItem getFromItem(): 获取FROM子句中的表或子查询。
void setFromItem(FromItem fromItem): 设置FROM子句中的表或子查询。

获取和设置选择项(SelectItems):
List<SelectItem> getSelectItems(): 获取SELECT子句中的选择项列表。
void setSelectItems(List<SelectItem> selectItems): 设置SELECT子句中的选择项列表。

获取和设置WHERE子句:
Expression getWhere(): 获取WHERE子句的条件表达式。
void setWhere(Expression where): 设置WHERE子句的条件表达式。

获取和设置GROUP BY子句:
List<Expression> getGroupByColumnReferences(): 获取GROUP BY子句中的列引用列表。
void setGroupByColumnReferences(List<Expression> groupByColumnReferences): 设置GROUP BY子句中的列引用列表。

获取和设置ORDER BY子句:
List<OrderByElement> getOrderByElements(): 获取ORDER BY子句中的排序元素列表。
void setOrderByElements(List<OrderByElement> orderByElements): 设置ORDER BY子句中的排序元素列表。

获取和设置LIMIT子句:
Limit getLimit(): 获取LIMIT子句。
void setLimit(Limit limit): 设置LIMIT子句。

获取和设置DISTINCT关键字:
boolean isDistinct(): 检查SELECT语句是否使用了DISTINCT关键字。
void setDistinct(boolean distinct): 设置SELECT语句是否使用DISTINCT关键字。

获取和设置INTO子句(用于SELECT INTO语句):
SubSelect getIntoTables(): 获取INTO子句中的表。
void setIntoTables(SubSelect intoTables): 设置INTO子句中的表。

获取和设置HAVING子句:
Expression getHaving(): 获取HAVING子句的条件表达式。
void setHaving(Expression having): 设置HAVING子句的条件表达式。

获取和设置别名:
String getAlias(): 获取SELECT语句的别名。
void setAlias(String alias): 设置SELECT语句的别名。

获取和设置子查询(SubSelect):
SubSelect getSubSelect(): 获取子查询。
void setSubSelect(SubSelect subSelect): 设置子查询。

获取和设置联合查询(Union):
List<PlainSelect> getUnion(): 获取联合查询的SELECT语句列表。
void setUnion(List<PlainSelect> union): 设置联合查询的SELECT语句列表。

构建SQL能力

构建SQL其实相当于解析SQL的逆过程,是构造组装一个PlainSelect的过程,进而得到SQL语句。

可以从无到有构造SQL,也可以基于原有SQL,解析出PlainSelect对象后,再进行修改。

操作方法很简单,直接看下方示例,企业实战中,通常会搭配自定义语义解析模板使用。

【示例代码】

@Test
public void buildSqlTemp() throws JSQLParserException {

    // 定义表
    Table table = new Table().withName("zoepres.pres_outp_pres_master")
            .withAlias(new Alias("t", false));

    // 定义查询列
    Column columnA = new Column().withColumnName("patient_id");
    Column columnB = new Column().withColumnName("pres_no");
    Column columnC = new Column().withColumnName("event_no");

    // 定义查询列的条件
    Expression whereExpression = new EqualsTo().withLeftExpression(columnA)
            .withRightExpression(new StringValue("5000022928"));

    // 定义查询
    PlainSelect select = new PlainSelect().addSelectItem(new LongValue(123))
            .withFromItem(table)
            .withWhere(whereExpression);

    // 增加函数表达式
    select.addSelectItem(columnB, new Alias("presNo"));

    // 增加子查询
    Expression expr2 = CCJSqlParserUtil.parseExpression("(select count(1) from dual)");
    select.addSelectItem(expr2, new Alias("id124"));

    // 增加函数处理逻辑
    Function function = new Function();
    function.setName("REPLACE");
    List<Expression> parameters = new ArrayList<>();
    parameters.add(columnC);
    parameters.add(new StringValue("a"));
    parameters.add(new StringValue("b"));
    function.setParameters(new ExpressionList(parameters));
    select.addSelectItem(function);

    System.out.println(select);
}

//输出信息如下:
SELECT 123,
       pres_no AS presNo,
       (SELECT count(1) FROM dual) AS id124,
       REPLACE(event_no, 'a', 'b')
  FROM zoepres.pres_outp_pres_master t
 WHERE patient_id = '5000022928'

企业实战

运用场景

Java SQL 解析器(如 JSQLParser)在多个场景中发挥着重要作用,以下是一些具体的应用场景:
1、构建数据库管理工具:在开发数据库客户端或管理界面时,Java SQL 解析器可以解析用户输入的 SQL 查询。这使得工具能够执行相应的操作,例如执行查询、更新数据库结构或管理数据,从而提升用户体验和操作效率。
2、实现自定义的 SQL 分析工具:当需要对大量 SQL 查询进行深入分析时,Java SQL 解析器可以帮助解析这些查询,以识别查询模式、性能瓶颈等。通过编写自定义分析逻辑,开发者可以获得更深入的洞察,优化数据库性能。
3、定制 SQL 解析和执行逻辑:在某些情况下,标准的数据库接口可能无法满足特定需求。此时,Java SQL 解析器可以解析 SQL 查询,并允许开发者编写自定义的执行逻辑,以实现更复杂的功能或满足特定的业务需求。
4、实现查询优化器:如果希望深入了解查询优化器的工作原理,Java SQL 解析器可以解析 SQL 查询,并基于解析结果实现自己的查询优化器。这为开发者提供了一个实验和学习的机会,以优化查询性能。
5、实现 SQL 注入检测工具:SQL 注入是常见的安全漏洞之一。为了防止 SQL 注入攻击,开发者可使用Java 的SQL解析器解析用户输入的 SQL 查询,并检测其中是否包含潜在的注入漏洞。这种检测机制有助于增强应用程序的安全性,保护数据库免受攻击。
通过这些应用场景,可以看出 Java SQL 解析器在数据库管理、性能优化、安全检测等方面的重要性,帮助开发者更高效地处理 SQL 查询和相关操作。

企业实战 - SQL 解析

【场景说明】

博主所在公司开发了数据中心产品线,涉及较多与数据打交道的数据中心工具和产品,诸如票据设计器、报表系统、床位导航等,这些产品都涉及可视化配置界面,需要通过工程人员编写的SQL语句,作为页面的数据来源,同时需要具备从SQL中解析出各项元素,进行后续操作。例如,提取出SELECT后面的元素,生成返回值表格列,允许设置表名、宽度、类型等内容。

【场景使用】

针对上述场景,使用JSQLParser来实现,就相当简单了,很轻易的将SQL语法进行剖析,分解出各个组成部分,一览无余,按需使用。

只能说使用JSQLParser来解析SQL,真是庖丁解牛、游刃有余。

企业实战 - SQL 生成

【场景说明】

无独有偶,博主所在公司继续开发低代码平台,部分产品线决定不通过写SQL完成配置,而是完全采用通俗易懂的拖拉拽和可视化表单编辑等方式,生成页面逻辑。为此,我们需要将前端元素转换为一个可执行的SQL语法。

【场景使用】

针对上述场景,使用JSQLParser来实现,也是相当简单了。

当然,我们没有直接解析前端传参,然后进行JSQLParser处理,而是在中间增加了一层JSON模板,前端根据该模板约定组装入参,后端增加将JSON模板使用JSQLParser解析成相应SQL。通常这种方式,使查询语义可以在各个产品线更好的共享,增强了扩展性。


总结陈词

本篇文章大体介绍了JSqlParser实现SQL的解析和生成,由于篇幅所限,仅展示简单示例分析。

💗 后续也会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

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

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

相关文章

MySQL底层概述—7.优化原则及慢查询

大纲 1.Explain概述 2.Explain详解 3.索引优化数据准备 4.索引优化原则详解 5.慢查询设置与测试 6.慢查询SQL优化思路 1.Explain概述 使用Explain关键字可以模拟查询优化器来执行SQL查询语句&#xff0c;从而知道MySQL是如何处理SQL语句的&#xff0c;从而分析出查询语句…

从扩散模型开始的生成模型范式演变--SDE

SDE是在分数生成模型的基础上&#xff0c;将加噪过程扩展时连续、无限状态&#xff0c;使得扩散模型的正向、逆向过程通过SDE表示。在前文讲解DDPM后&#xff0c;本文主要讲解SDE扩散模型原理。本文内容主要来自B站Up主deep_thoughts分享视频Score Diffusion Model分数扩散模型…

NeuIPS 2024 | YOCO的高效解码器-解码器架构

该研究提出了一种新的大模型架构&#xff0c;名为YOCO&#xff08;You Only Cache Once&#xff09;&#xff0c;其目的是解决长序列语言模型推理中的内存瓶颈。YOCO通过解码器-解码器结构的创新设计&#xff0c;显著减少推理时的显存占用并提升了长序列的处理效率。 现有大模…

Android 设备使用 Wireshark 工具进行网络抓包

背景 电脑和手机连接同一网络&#xff0c;想使用wireshark抓包工具抓取Android手机网络日志&#xff0c;有以下两种连接方法&#xff1a; Wi-Fi 网络抓包。USB 网络共享抓包。需要USB 数据线将手机连接到电脑&#xff0c;并在开发者模式中启用 USB 网络共享。 查看设备连接信…

腾讯云 AI 代码助手:单元测试应用实践

引言 在软件开发这一充满创造性的领域中&#xff0c;开发人员不仅要构建功能强大的软件&#xff0c;还要确保这些软件的稳定性和可靠性。然而&#xff0c;开发过程中并非所有任务都能激发创造力&#xff0c;有些甚至是重复且乏味的。其中&#xff0c;编写单元测试无疑是最令人…

修改Docker 默认存储目录( Docker Root Dir: /var/lib/docker)

Docker 默认将所有的数据&#xff08;包括镜像、容器、卷等&#xff09;存储在 /var/lib/docker 目录下。这个目录默认被配置在系统的根分区或者较小的分区上。随着容器化应用的增加&#xff0c;或者 Docker 容器和镜像的数量增加&#xff0c;默认存储位置可能会迅速填满&#…

芯片测试-射频中的单位

射频中的单位 &#x1f4a2;dB&#xff0c;dBc&#x1f4a2;&#x1f4a2;dB&#x1f4a2;&#x1f4a2;dBc&#x1f4a2;&#x1f4a2;3dB和0dB&#x1f4a2; &#x1f4a2;dBm和dBw&#x1f4a2;&#x1f4a2;dBuV&#xff0c;dBmV和dBV&#x1f4a2;&#x1f4a2;dBuV&#…

hls视频流学习

hls格式播放的依赖安装&#xff1a; <!-- 新增hls播放库 -->npm install hls.js 组件封装&#xff1a; <template><div class"hls-player-cls"><video ref" video" controls style"width: 100%; max-width: 800px;">…

SAP Native SQL 的简单说明

Open SQL访问数据字典中声明的数据库表&#xff0c;不区分数据库类型&#xff0c;执行时会自动转换为对应的语句&#xff0c;且可以使用本地缓存。Native SQL使用特定于数据库的SQL语句,但是可以访问比Open SQL 更多的表&#xff0c;更多的操作&#xff0c;缺点也很明显&#x…

Python学习笔记之IP监控及告警

一、需求说明 作为一名运维工程师&#xff0c;监控系统必不可少。不过我们的监控系统往往都是部署在内网的&#xff0c;如果互联网出口故障&#xff0c;监控系统即使发现了问题&#xff0c;也会告警不出来&#xff0c;这个时候我们就需要补充监控措施&#xff0c;增加从外到内的…

QT去除窗口边框(无边框)

ch21_TencentMeetingLogin::ch21_TencentMeetingLogin(QWidget *parent): QDialog(parent) {ui.setupUi(this);this->setWindowFlags(Qt::FramelessWindowHint);//去除窗口边框 } 但此时窗口不能拖动且点击任务栏程序图标不能最小化&#xff01; this->setWindowFlags(Q…

vue实现弹窗输入验证码

实现思路&#xff1a;前端输入完账号和密码&#xff0c;点击登录按钮的时候&#xff0c;弹出一个输入验证码的窗口&#xff0c;后端把验证码图片通过base64的字符传给前端&#xff0c;前端把字符当成图片展示出来。输入完验证码&#xff0c;点击确认进行登录&#xff0c;把验证…

1.Shell变量

1.shell变量介绍 Linux Shell中的变量分为&#xff1a; 系统变量 和 用户自定义变量系统变量&#xff1a;$HOME、$PWD、$SHELL、$USER等等, 比如 echo $HOME显示当前shell所有变量 :set 2.自定义变量 2.1 语法规则 # 基本语法 1. 定义变量: 变量名值 2. 撤销变量: unset变量…

多线程篇-4--重点概念1(volatile,Synchronized,内存屏障,MESI协议)

一、volatile &#xff08;1&#xff09;、简述 volatile是java提供的一个关键字&#xff0c;英文意思为不稳定的。 可以保障被声明对象的可见性和一定程度上的有序性&#xff0c;但不能保证操作的原子性。 当一个变量被声明为volatile时&#xff0c;意味着该变量的值会直接从…

使用Yarn标签能力实现任务资源隔离

我们在工作或自己研究的时候&#xff0c;一般不关注yarn的lable能力&#xff0c;这主要因为业内大部分用的是公有云&#xff0c;即使有私有云或者是独占集群&#xff0c;集群的运维通常也是抛给厂商了&#xff0c;所以这就导致Label的能力被忽视了&#xff0c;并且这个能力默认…

大数据新视界 -- 大数据大厂之 Hive 数据质量监控:实时监测异常数据(下)(18/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

基础(函数、枚举)错题汇总

枚举默认从0开始&#xff0c;指定后会按顺序赋值 而这个枚举变量X&#xff0c;如果在全局&#xff08;函数外部&#xff09;定义&#xff0c;那默认为0&#xff0c;如果在函数内部&#xff08;局部变量&#xff09;&#xff0c;那就是随机值&#xff0c;必须初始化。 枚举变量…

[RabbitMQ] 延迟队列+事务+消息分发

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

[GKCTF 2021]签到

[GKCTF 2021]签到 wireshark跟踪http流&#xff0c;基本编解码&#xff0c;倒叙&#xff0c;栅栏密码 找到cat /f14g 把包里返回的字符串先hex解码&#xff0c;再base64解码&#xff0c;看到一个时间是倒叙&#xff0c;不含flag 继续往下面翻&#xff0c;可以看到cat%2Ff14g%7…

后端-mybatis的一对一查询

准备两张表菜单表和分类表&#xff0c;一个菜单对应一个分类&#xff0c;一个分类对应多个菜单&#xff0c;我们从菜单对分类来操作一对一的关系&#xff0c;首先在菜单表里面要有一个分类表的id字段。 使用mapper的映射文件.xml来写的话我们在resource目录下创建各自mapper的…