当前位置: 首页 > news >正文

【JSqlParser】Java使用JSqlParser解析SQL语句总结

简述

Java解析SQL语句有很多工具都可以做到,比如Mybatis、Druid、目前用来用去最全面的仍然是Jsqlparser,它是一个Github上的开源项目,JSqlParser是一个用于解析SQL语句的Java库,它可以帮助开发者分析和操作SQL语句的结构。无论是从事数据库开发、SQL性能优化,还是需要解析SQL语句以进行其他操作,JSqlParser都能提供强大的支持

特点

  1. 支持多种SQL方言:JSqlParser支持多种数据库的SQL方言,包括MySQL、Oracle、PostgreSQL等,这使得在不同数据库之间进行SQL语句解析变得更加方便。

  2. 灵活的操作:JSqlParser以多种方式操作SQL语句,例如修改查询条件、提取表名、列名等,甚至整个SQL语句中使用到的函数,从而满足各种需求。

  3. 易于集成:JSqlParser可以轻松集成到您的Java项目中,只需将其作为依赖项添加到项目中即可。

  4. 社区支持:JSqlParser拥有一个活跃的社区,许多开发者为其提供贡献,使得这个工具不断得到改进和优化,它的主要操刀人manticore-projects (github.com)也非常负责并愿意解答各种问题和参与讨论

环境准备

将Jsqlparser直接添加到项目中

Maven:

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

Gradle:

implementation("com.github.jsqlparser:jsqlparser:4.9")

快速使用

使用原则

假设现在有一条简单的SQL语句需要拿来解析,首先应该保证这个SQL在结构上是没有问题的,最好是放在数据库中可以直接运行的,不夹杂不应该的标点符号,那样解析起来才不会出错

使用案例:

以下是一个简单的SQL语句,并且这句SQL没有违反原则,是一条基本可以正常运行的SQL语句

SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= "刘"

解析SQL语句

接下来使用Jsqlparser去解析语句,其中第二行则是最基本的,将SQL语句字符串拿来解析,如果这句SQL语句违反了原则,例如存在特殊标点符号或者不符合SQL语句,那么在第二行就会产生异常

String sql = "SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= '刘'";
// Parse SQL
Statement statement = CCJSqlParserUtil.parse(sql);
Select selectStatement = (Select) statement;
log.info("==> JsqlParser SQL: {}", selectStatement.toString());

正常情况下,将得到一个包含各种属性的statement,这意味着一条SQL成功被解析,并被赋予到一个对象的各个属性中

认识Statement

熟悉JDBC的程序员一般都知道Statement,其实就是语句的意思,不过在Jsqlparser中Statement已经面向对象,被设计成了一个interface,之所以设计成interface大概都可以猜到,因为Jsqlparser既然要去解析SQL,那必然要对SQL语句做区分,到底是Select、还是Insert、还是Delete、甚至是Create,而Jsqlparser对每种语句都做了一个封装,它们都继承了Statement

所以一条SQL语句,根据不同情况,都有适配的对象,例如Select语句对应着net.sf.jsqlparser.statement.select.Select对象,而Insert也有自己的对象,所以我们都可以通过将Statement强转为它所对应的对象来获取或改变其中的属性,这也是解析SQL的一大目的

其实在Jsqlparser成功解析SQL语句之后,statement就已经有了它的类型

String sql = "SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= '刘'";// Parse SQLStatement statement = CCJSqlParserUtil.parse(sql);if(statement instanceof Select){Select selectStatement = (Select) statement;log.info("==> JsqlParser SQL: {}", selectStatement.toString());
}
if(statement instanceof Insert){Insert insertStatement = (Insert) statement;log.info("==> JsqlParser SQL: {}", insertStatement.toString());
}
if(statement instanceof Update){Update updateStatement = (Update) statement;log.info("==> JsqlParser SQL: {}", updateStatement.toString());
}
if (statement instanceof Delete) {Delete deleteStatement = (Delete) statement;log.info("==> JsqlParser SQL: {}", statement.toString());
}

分析语句

查询语句

在statement成功解析SQL语句之后,通过PlainSelect就可以拿到SQL语句中的各个元素

String sql = "SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= '刘'";
// Parse SQL
Statement statement = CCJSqlParserUtil.parse(sql);
if(statement instanceof Select){Select selectStatement = (Select) statement;log.info("==> JsqlParser SQL: {}", selectStatement.toString());PlainSelect plainSelect = selectStatement.getPlainSelect();log.info("==> FromItem: {}", plainSelect.getFromItem());log.info("==> SelectItem: {}",plainSelect.getSelectItems());log.info("==> Where: {}",plainSelect.getWhere());
}

运行结果:

==> JsqlParser SQL: SELECT id, name, nickname, age, job, department FROM staff_member WHERE nickname = '刘'
==> FromItem: staff_member
==> SelectItem: [id, name, nickname, age, job, department]
==> Where: nickname = '刘'
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语句列表。

新增语句

新增语句和查询语句一样,只不过由于Insert没有Select语句那么复杂,所以Jsqlparsert并没有专门设计一个类似PlainSelect extend Select这样一个类,而是直接通过Insert对象就可以获取和操作,Insert语句中的内容

String sql = "INSERT INTO employees (employee_id, employee_name, department) VALUES (1, 'John Doe', 'Human Resources')";
// Parse SQL
Statement statement = CCJSqlParserUtil.parse(sql);
if (statement instanceof Insert) {Insert insertStatement = (Insert) statement;log.info("==> JsqlParser SQL: {}", insertStatement.toString());log.info("==> Table: {}", insertStatement.getTable());log.info("==> Columns: {}", insertStatement.getColumns());log.info("==> ItemsList: {}", insertStatement.getValues());
}

运行结果:

 ==> JsqlParser SQL: INSERT INTO employees (employee_id, employee_name, department) VALUES (1, 'John Doe', 'Human Resources')==> Table: employees==> Columns: employee_id, employee_name, department==> ItemsList: VALUES (1, 'John Doe', 'Human Resources')
Insert常用方法
  • Table getTable(): 获取插入语句中的目标表。
  • List<Column> getColumns(): 获取插入语句中要插入的列的列表。
  • ItemsList getValues(): 获取插入语句中的值列表,可以是单个值或者子查询。
  • String getPrefix(): 获取INSERT关键字前的前缀,如INSERT INTO或者INSERT IGNORE
  • void setTable(Table table): 设置插入语句中的目标表。
  • void setColumns(List<Column> columns): 设置插入语句中要插入的列的列表。
  • void setValues(ItemsList values): 设置插入语句中的值列表。
  • void setPrefix(String prefix): 设置INSERT关键字前的前缀。

更新语句

Update和Insert是一样的,内容相对于Select较为简单,通过Update对象即可获得相关内容

String sql = "UPDATE employees SET department = 'Human Resources' WHERE employee_id = 1";
// Parse SQL
Statement statement = CCJSqlParserUtil.parse(sql);
if (statement instanceof Update) {Update updateStatement = (Update) statement;log.info("==> JsqlParser SQL: {}", updateStatement.toString());Table table = updateStatement.getTable();log.info("Table Name: {}", table.getName());log.info("==> Columns: {}", updateStatement.getColumns());// 获取更新项List<UpdateSet> updateSets = updateStatement.getUpdateSets();for (UpdateSet updateSet : updateSets) {for (Expression expression : updateSet.getColumns()) {log.info("==> Expression: {}", expression.toString());}}log.info("==> ItemsList: {}", updateStatement.getExpressions());Expression where = updateStatement.getWhere();log.info("==> Where: {}", where.toString());
}

运行结果

==> JsqlParser SQL: UPDATE employees SET department = 'Human Resources' WHERE employee_id = 1
Table Name: employees
==> Columns: department
==> Expression: department
==> ItemsList: 'Human Resources'
==> Where: employee_id = 1

删除语句

        String sql = "DELETE FROM table_name WHERE condition";Statement statement = CCJSqlParserUtil.parse(sql);if (statement instanceof Delete) {Delete deleteStatement = (Delete) statement;// 获取要删除的表Table table = deleteStatement.getTable();System.out.println("Table Name: " + table.getName());// 获取WHERE条件Expression where = deleteStatement.getWhere();System.out.println("Where Condition: " + where.toString());}

运行结果:

Table Name: table_name
Where Condition: condition

从SQL语句中提取表名

Statement statement = CCJSqlParserUtil.parse("SELECT * FROM MY_TABLE1");
Select selectStatement = (Select) statement;
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
List<String> tableList = tablesNamesFinder.getTableList(selectStatement);

最终tableList里将存入所有给出的SQL语句中的表名,以上案例只有一个表名

为SQL语句各个字段表达式添加别名

String sql = "SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= '刘'";
// Parse SQL
Statement statement = CCJSqlParserUtil.parse(sql);
if(statement instanceof Select ){Select selectStatement = (Select) statement;final AddAliasesVisitor instance = new AddAliasesVisitor();instance.setPrefix("t");selectStatement.accept(instance);log.info("==> JSqlParser finalSQL: {}", selectStatement);
}

动态加字段加表达式加条件

使用SelectUtils,为一个Select语句,增加查询的字段
Select select = (Select) CCJSqlParserUtil.parse("select mydate from mytable");
SelectUtils.addExpression(select, new Column("mylocation"));
增加一个表达式
Select select = (Select) CCJSqlParserUtil.parse("select a from mytable");
SelectUtils.addExpression(select, new Column("b"));
assertEquals("SELECT a, b FROM mytable", select.toString());Addition add = new Addition();
add.setLeftExpression(new LongValue(5));
add.setRightExpression(new LongValue(6));
SelectUtils.addExpression(select, add);assertEquals("SELECT a, b, 5 + 6 FROM mytable", select.toString());
增加一个Join

动态添加Join,可以为Join增加表达式,以及设置Join的表,并且通过setLeft()、setRight()、setInner()可以设置join的方向,最终它会生成对应的SQL语句

Select select = (Select) CCJSqlParserUtil.parse("select a from mytable");
final EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(new Column("a"));
equalsTo.setRightExpression(new Column("b"));
Join addJoin = SelectUtils.addJoin(select, new Table("mytable2"), equalsTo);
addJoin.setLeft(true);
assertEquals("SELECT a FROM mytable LEFT JOIN mytable2 ON a = b", select.toString());
用SelectUtils构建一个SQL语句

下面是SelectUtils里面的一些方法,可以看到不光是为查询语句增加表达式、Join和分组,其次还可以使用build等方法去构建一个SQL语句

这里是一个案例,构建了一个查询语句,其中也使用到了addGroupBy

Select select = SelectUtils.buildSelectFromTableAndExpressions(new Table("mytable"),new Column("a"), new Column("b"));
SelectUtils.addExpression(select, new Column("c"));
final EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(new Column("id"));
equalsTo.setRightExpression(new Column("1"));
SelectUtils.addGroupBy(select, new Column("d"));
log.info("==> JsqlParser Build SQL: {}", select.toString());

输出结果:

==> JsqlParser Build SQL: SELECT a, b, c FROM mytable GROUP BY d

简短的总结

上面的代码虽然不少,但实际上真正需要熟悉的只有一个,就是直接调用CCJSqlParserUtil.parse(sql);去获得Statement,然后通过Statement去操作和获取解析后的SQL中的内容,非常简单方便

实际应用场景

说了那么多JSQLPARSER的使用,或许很多朋友并不能联想到有哪些具体可以用到它的地方,实际上想要开发一个优秀的软件产品,那么细节是少不了的,SQL是BS软件的本质之一,那么针对SQL,我们能做的还有很多,以下列举几个常见的场景

  • SQL审计和分析:

    • 审计SQL语句,检查是否包含潜在的安全漏洞,如SQL注入。
    • 分析SQL语句的性能,检查是否存在可以优化的查询条件。
  • 数据库迁移和同步:

    • 在迁移数据库时,使用JSqlParser解析源数据库的SQL语句,并生成目标数据库的相应语句。
    • 数据库同步工具可以使用JSqlParser来解析和生成SQL语句,以实现数据的同步。
  • 动态SQL生成:

    • 应用程序需要生成动态SQL语句以执行不同的操作,JSqlParser可以用来解析这些动态生成的SQL语句。
  • SQL测试和验证:

    • 在开发过程中,使用JSqlParser来验证SQL语句的正确性。
    • 单元测试中,使用JSqlParser来解析和执行测试用例中的SQL语句。
  • SQL注入防护:

    • 在应用程序中,使用JSqlParser来解析和分析用户输入的SQL查询,以防止SQL注入攻击。
  • 数据库管理工具:

    • 数据库管理工具可以使用JSqlParser来解析和显示SQL语句的结构,帮助开发者理解查询的逻辑。
  • 代码生成:

    • 在生成数据库访问层代码时,使用JSqlParser来解析SQL语句,并生成相应的数据访问对象(DAO)或查询对象(DTO)。
  • SQL格式化:

    • 使用JSqlParser来格式化SQL语句,使其更易于阅读和理解。
  • SQL优化:

    • 通过分析SQL语句的结构,可以提出性能优化建议。
  • 数据处理工具:

    • 在数据处理和转换工具中,使用JSqlParser来解析和生成SQL语句,以实现数据的导入和导出。

在Springboot+Mybaits中使用

如果使用纯原生Mybatis那么我们需要手动在maven中加入Jsqlparser的支持,但如果使用Mybatis plus,那么就无需自己再引用,Mybaits plus自带Jsqlparser

上面举的很多例子都很简单,拿一个SQL语句解析而已,这种情况是手动化的,通常见于单元测试等情况,但如果在项目中想要通过被动的方式,让项目自己去解析SQL语句,就需要分析项目的具体情况,例如在Mybatis中通过Interceptor就可以获得到项目中真正去执行的SQL语句,详见:Mybatis 的 Interceptor(拦截器) 与 JSqlparser 结合解析SQL 使SpringBoot项目多数据库兼容的尝试_mybatis设置jsqlparser-CSDN博客

通过Mybatis的拦截器,我们拿到了项目执行的SQL语句,再通过Jsqlparser去解析,并做一定的处理,例如以上提到的那些实际应用场景

高级特性(很实用)

Jsqlparser在解析SQL语句的过程中,每一个节点都会被解析成一个叫SimpleNode的对象,它包含着各个节点的属性,这仿佛就像Dom4j解析XML的时候所有的元素都视为Node一样,解析之后的内容都是节点,而循环这些节点,Jsqlparser给出了相应的方法,提供了用于遍历节点的接口CCJSqlParserVisitor,而它的默认实现则是CCJSqlParserDefaultVisitor,在这里创建一个自己的类,并通过继承 CCJSqlParserDefaultVisitor 重写它的visit 方法,便可以实现自己的策略,更加方便的去操作解析内容

public class SQLModifier extends CCJSqlParserDefaultVisitor {@Overridepublic Object visit(SimpleNode node, Object data) {Object value = node.jjtGetValue();switch (node.getId()) {case CCJSqlParserTreeConstants.JJTTABLENAME:break;case CCJSqlParserTreeConstants.JJTCOLUMN:break;case CCJSqlParserTreeConstants.JJTFUNCTION:break;default:break;}return super.visit(node, data);}
}

调用自定义的Visitor

String originalSql = "select * from user where id = 1";
CCJSqlParser parser = CCJSqlParserUtil.newParser(originalSql);
Statement statement = parser.Statement();
parser.getASTRoot().jjtAccept(sqlTestModifier, null);

以上代码做了一个自定义的visitor,重写的visit方法中可以看到形参SimpleNode,而调用这个自定义的Visitor之后,语句则会被拆解,依次进入到visit方法中,通过node.jjtGetValue可以获得节点信息,而node.getId()实则是获取节点的类型,而Switch-case中的常量分别代表了在解析SQL语句时,生成的抽象语法树AST (abstract syntax tree)中不同类型的节点,每个节点对应一个特定的SQL构造,如SELECT、FROM、WHERE等。下面是对这些常量代表的SQL构造的简要说明:

  • JJTSTATEMENT: 代表一个SQL语句。
  • JJTVOID: 可能代表一个空语句或者不返回结果的语句。
  • JJTBLOCK: 代表一个语句块,可能包含多个语句。
  • JJTSTATEMENTS: 代表一个包含多个语句的列表。
  • JJTCOLUMN: 代表一个列名。
  • JJTTABLENAME: 代表一个表名。
  • JJTSELECT: 代表一个SELECT查询。
  • JJTPARENTHESEDSELECT: 代表被括号包围的SELECT查询。
  • JJTLATERALVIEW: 代表LATERAL VIEW子句,常用于Hive SQL。
  • JJTFORCLAUSE: 代表FOR子句。
  • JJTLATERALSUBSELECT: 代表LATERAL子查询。
  • JJTPLAINSELECT: 代表一个简单的SELECT查询(不包含UNION等)。
  • JJTSETOPERATIONLIST: 代表一个集合操作列表,比如UNION, EXCEPT, INTERSECT。
  • JJTWITHITEM: 代表WITH子句中的单个项。
  • JJTSELECTITEM: 代表SELECT子句中的一个项,可能是列名、表达式等。
  • JJTJOINEREXPRESSION: 代表JOIN操作的表达式。
  • JJTLIMITWITHOFFSET: 代表LIMIT和OFFSET子句。
  • JJTPLAINLIMIT: 代表一个简单的LIMIT子句。
  • JJTEXPRESSION: 代表一个表达式。
  • JJTREGULARCONDITION: 代表一个常规条件(如WHERE子句中的条件)。
  • JJTINEXPRESSION: 代表IN表达式。
  • JJTLIKEEXPRESSION: 代表LIKE表达式。
  • JJTSIMILARTOEXPRESSION: 代表SIMILAR TO表达式。
  • JJTISDISTINCTEXPRESSION: 代表IS DISTINCT FROM表达式。
  • JJTEXPRESSIONLIST: 代表一个表达式列表。
  • JJTPRIMARYEXPRESSION: 代表一个主要表达式。
  • JJTCONNECTBYROOTOPERATOR: 代表CONNECT BY ROOT操作符。
  • JJTCASEWHENEXPRESSION: 代表CASE WHEN表达式。
  • JJTFUNCTION: 代表一个函数调用。
  • JJTSEQUENCE: 代表一个序列。
  • JJTSYNONYM: 代表一个同义词。

Visit常见应用场景

目前我们知道,通过Mybatis 的 interceptor可以拦截到所有执行的SQL语句,而在 自定义的interceptor中调用自定义的visit,就可以对项目中所有运行的SQL做一个拦截并处理,那么具体可以做哪些骚操作呢

  1. SQL语句重写:在某些数据库系统中,为了优化性能或满足特定的需求,可能需要重写SQL语句。通过自定义访问者,可以在AST(abstract syntax tree)层面进行这些操作

  2. 元数据提取:自定义访问者可以用来提取SQL语句中的元数据,比如查询涉及的所有表名、列名、函数等,这些信息可以用于构建数据库的概要图或进行数据治理。

  3. 数据屏蔽:在需要对敏感数据进行屏蔽的应用中,可以通过自定义访问者来识别并修改涉及敏感数据的查询,以确保在查询结果中不会暴露敏感信息。

  4. 动态查询构建:在需要动态构建SQL查询的应用中,可以通过自定义访问者来解析模板SQL语句,并根据实际参数动态替换模板中的占位符,从而构建出完整的SQL语句。

  5. 缓存策略决策:根据SQL查询的特征,可以通过自定义访问者来判断查询结果是否适合缓存,以及应该使用什么样的缓存策略。

总结

Jsqlparser非常容易上手使用,而它也解决了解析SQL语句的问题,结合Springboot 和 mybatis,可以设计自定义插件,就像Mybatis plus的分页插件那样,可以开发自己系统需求的业务处理功能,方便项目业务的时间,甚至可以拿来提高效率,毕竟总有一些时候,对SQL的解析是绕不开的。

相关文章:

【JSqlParser】Java使用JSqlParser解析SQL语句总结

简述 Java解析SQL语句有很多工具都可以做到&#xff0c;比如Mybatis、Druid、目前用来用去最全面的仍然是Jsqlparser&#xff0c;它是一个Github上的开源项目&#xff0c;JSqlParser是一个用于解析SQL语句的Java库&#xff0c;它可以帮助开发者分析和操作SQL语句的结构。无论是…...

TCP断开通信前的四次挥手(为啥不是三次?)

1.四次握手的过程 客户端A发送 FIN&#xff08;终止连接请求&#xff09; A&#xff1a;我要断开连接了&#xff08;FIN&#xff09;。A进入FIN_WAIT_1状态&#xff0c;表示请求断开&#xff0c;等待对方确认。 服务器B回复 ACK&#xff08;确认断开请求&#xff0c;但还未准备…...

解决用 rm 报bash: /usr/bin/rm: Argument list too long错

但目录里面文件过多用 rm 报bash: /usr/bin/rm: Argument list too long错时怎么办&#xff1a; 看看以下操作记录 rootmcu:/# cd /tmp rootmcu:/tmp# rm -f /tmp/chunk* bash: /usr/bin/rm: Argument list too long rootmcu:/tmp# rm -rf /tmp/chunk* bash: /usr/bin/rm: Arg…...

AI News(1/21/2025):OpenAI 安全疏忽:ChatGPT漏洞引发DDoS风险/OpenAI 代理工具即将发布

1、OpenAI 的安全疏忽&#xff1a;ChatGPT API 漏洞引发DDoS风险 德国安全研究员 Benjamin Flesch 发现了一个严重的安全漏洞&#xff1a;攻击者可以通过向 ChatGPT API 发送一个 HTTP 请求&#xff0c;利用 ChatGPT 的爬虫对目标网站发起 DDoS 攻击。该漏洞源于 OpenAI 在处理…...

从零到上线:Node.js 项目的完整部署流程(包含 Docker 和 CICD)

从零到上线&#xff1a;Node.js 项目的完整部署流程&#xff08;包含 Docker 和 CI/CD&#xff09; 目录 项目初始化&#xff1a;构建一个简单的 Node.js 应用设置 Docker 环境&#xff1a;容器化你的应用配置 CI/CD&#xff1a;自动化构建与部署上线前的最后检查&#xff1a;…...

哈希桶(开散列)

文章目录 前言实现插入put方法实现get方法实现泛型类哈希桶 前言 哈希桶用来解决哈希冲突&#xff0c;牺牲空间换取时间。 通过数组和链表来实现哈希桶 public class Node{public int key;public int value;public Node next;public Node(int key,int value){this.keykey;this…...

DEBERTA:具有解耦注意力机制的解码增强型BERT

摘要 近年来&#xff0c;预训练神经语言模型的进展显著提升了许多自然语言处理&#xff08;NLP&#xff09;任务的性能。本文提出了一种新的模型架构DeBERTa&#xff08;具有解耦注意力机制的解码增强型BERT&#xff09;&#xff0c;通过两种新技术改进了BERT和RoBERTa模型。第…...

WWW2025 多模态对话系统意图识别挑战赛方案总结

WWW2025 多模态对话系统意图识别挑战赛方案 代码实现&#xff1a;https://github.com/klayc-gzl/incent_internvl_2.5_8b 最终成绩&#xff1a; 大赛背景 互联网已成为提供客户服务的主要沟通渠道。网络客户服务面临的一个关键挑战是服务对话中多模态意图的高效识别。通过利…...

渗透测试--攻击常见的Web应用

本文章咱主要讨论&#xff0c;常见Web应用的攻击手法&#xff0c;其中并不完全&#xff0c;因为Web应用是在太多无法囊括全部&#xff0c;但其中的手法思想却值得我们借鉴&#xff0c;所以俺在此做了记录&#xff0c;希望对大家有帮助&#xff01;主要有以下内容&#xff1a; 1…...

w173疫苗发布和接种预约系统

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…...

常用的跨域方案有哪些?

在前端开发中&#xff0c;跨域&#xff08;Cross-Origin&#xff09;是一个常见问题&#xff0c;通常是由于浏览器的同源策略&#xff08;Same-Origin Policy&#xff09;限制导致的。为了解决跨域问题&#xff0c;前端开发者可以采用多种方案。 1. CORS&#xff08;跨域资源共…...

JS通过ASCII码值实现随机字符串的生成(可指定长度以及解决首位不出现数值)

在之前写过一篇“JS实现随机生成字符串&#xff08;可指定长度&#xff09;”&#xff0c;当时写的过于简单和传统&#xff0c;比较粗放。此次针对此问题&#xff0c;对随机生成字符串的功能进行优化处理&#xff0c;对随机取到的字符都通过程序自动来完成。 在写之前&#xff…...

IoTDB 1.2 升级 1.3 后 Pipe 插件失效

问题现象 客户使用 Pipe 功能将数据从 IoTDB 系统传输至 Kafka 集群&#xff0c;以便进行后续的数据处理与分析。在从企业版 1.2.5 升级至企业版 1.3.3.6 后&#xff0c;客户反馈 Kafka 的 consumer 无法接收到数据。经检查日志&#xff0c;发现存在以下报错&#xff1a; 问题…...

基于STM32的智能门锁安防系统(开源)

目录 项目演示 项目概述 硬件组成&#xff1a; 功能实现 1. 开锁模式 1.1 按键密码开锁 1.2 门禁卡开锁 1.3 指纹开锁 2. 功能备注 3. 硬件模块工作流程 3.1 步进电机控制 3.2 蜂鸣器提示 3.3 OLED显示 3.4 指纹与卡片管理 项目源代码分析 1. 主程序流程 (main…...

浅谈云端编辑器,分析其亮点与不足

浅谈云端编辑器&#xff0c;分析其亮点与不足 这个云端编辑器界面可以分为左侧题目筛选栏、中间题目描述与代码编辑区域、右侧AI提示功能三部分。以下是详细的分析&#xff1a; 1. 左侧题目筛选栏 层次结构清晰&#xff1a;左侧栏展示了一个层级结构&#xff0c;题目按主题分…...

Python字符串引号的嵌套问题

目录 1、使用不同类型的引号 2、使用转义字符 3、使用三重引号 4、嵌套三重引号 5、注意事项 在Python中&#xff0c;字符串可以使用单引号 () 或双引号 (") 来定义&#xff0c;但是如果我们要定义的字符串内也包含引号&#xff0c;字符串界定符的不正确使用会导致语法…...

latex如何让目录后面有点

使用前效果 在导言区引入以下代码 \usepackage[subfigure]{tocloft} \usepackage{subfigure} % 设置目录中 section 条目前导符号为连续点 \renewcommand{\cftsecleader}{\cftdotfill{\cftdotsep}}编译后的效果...

【力扣系列题目】不同路径 组合总和 最大连续1个数 打家劫舍{持续更新中...}

文章目录 不同路径不同路径[不同路径 II](https://leetcode.cn/problems/unique-paths-ii/)[不同路径 III](https://leetcode.cn/problems/unique-paths-iii/) 组合总和组合总和 【无重复数字无限制选择次数】[组合总和 II](https://leetcode.cn/problems/combination-sum-ii/)…...

方法建议ChatGPT提示词分享

方法建议 ChatGPT能够根据您的具体需求提供针对性的建议&#xff0c;帮助您选择最合适的研究方法。通过清晰的提示&#xff0c;ChatGPT可以精准地为您提供最契合的研究方案。此外&#xff0c;它还能协助您将这些方法灵活地应用于新的研究环境&#xff0c;提出创新的技术解决方案…...

Cursor的详细使用指南

以下是一份关于 Cursor 的详细使用指南&#xff1a; 一、安装与设置 下载与安装&#xff1a; 首先&#xff0c;访问 Cursor 的官方网站&#xff0c;根据你的操作系统&#xff08;Windows、Mac 或 Linux&#xff09;下载相应的安装程序。运行安装程序&#xff0c;按照屏幕上的提…...

Python----Python高级(正则表达式:语法规则,re库)

一、正则表达式 1.1、概念 正则表达式&#xff0c;又称规则表达式,&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、 regexp或RE&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff0…...

电脑如何访问手机文件?

手机和电脑已经深深融入了我们的日常生活&#xff0c;无时无刻不在为我们提供服务。除了电脑远程操控电脑外&#xff0c;我们还可以在电脑上轻松地访问Android或iPhone手机上的文件。那么&#xff0c;如何使用电脑远程访问手机上的文件呢&#xff1f; 如何使用电脑访问手机文件…...

计算最接近的数

计算最接近的数 真题目录: 点击去查看 E B卷 100分题型 题目描述 给定一个数组X和正整数K&#xff0c;请找出使表达式&#xff1a; X[i] - X[i 1] - … - X[i K - 1] 结果最接近于数组中位数的下标 i &#xff0c;如果有多个 i 满足条件&#xff0c;请返回最大的 i. 其中&…...

Ubuntu离线docker compose安装DataEase 2.10.4版本笔记

1、先准备一个可以正常上网的相同版本的Ubuntu系统&#xff0c;可以使用虚拟机。Ubuntu系统需要安装好docker compose或docker-compose 2、下载dataease-online-installer-v2.10.4-ce.tar在线安装包&#xff0c;解压并执行install.sh进行安装和启动 3、导出docker镜像 sudo d…...

C#使用WMI获取控制面板中安装的所有程序列表

C#使用WMI获取控制面板中安装的所有程序列表 WMI 全称Windows Management Instrumentation,Windows Management Instrumentation是Windows中用于提供共同的界面和对象模式以便访问有关操作系统、设备、应用程序和服务的管理信息。如果此服务被终止&#xff0c;多数基于 Windo…...

WPF2-1在xaml为对象的属性赋值.md

1. AttributeValue方式 1.1. 简单属性赋值1.2. 对象属性赋值 2. 属性标签的方式给属性赋值3. 标签扩展 (Markup Extensions) 3.1. StaticResource3.2. Binding 3.2.1. 普通 Binding3.2.2. ElementName Binding3.2.3. RelativeSource Binding3.2.4. StaticResource Binding (带参…...

社区版Dify实现文生视频 LLM+ComfyUI+混元视频

社区版Dify实现文生视频 LLMComfyUI混元视频 一、 社区版Dify实现私有化混元视频效果二、为什么社区版Dify可以在对话框实现文生视频&#xff1f;LLMComfyUI混元视频 实现流程图&#xff08;重点&#xff09;1. 文生视频模型支持ComfyUI2. ComfyUI可以轻松导出API实现封装3. Di…...

QT调用OpenSceneGraph

OSG和osgQt编译教程&#xff0c;实测通过 一、下载OpenSceneGraph OpenSceneGraphhttps://github.com/openscenegraph/OpenSceneGraph 二、使用CMAKE编译OpenSceneGraph 1.打开cmake&#xff0c;配置源代码目录 2. CMAKE_INSTALL_PREFIX设置为install文件夹&#xff0c;生…...

Qt基础项目篇——Qt版Word字处理软件

一、核心功能 本软件为多文档型程序&#xff0c;界面是标准的 Windows 主从窗口 拥有&#xff1a;主菜单、工具栏、文档显示区 和 状态栏。 所要实现的东西&#xff0c;均在下图了。 开发该软件&#xff0c;主要分为下面三个阶段 1&#xff09;界面设计开发 多窗口 MDI 程序…...

【Postgres_Python】使用python脚本批量创建和导入多个PG数据库

之前批量创建和导入数据库分为2个python脚本进行&#xff0c;现整合优化代码合并为一个python脚本&#xff0c;可同步实现数据库的创建和数据导入。之前的文章链接&#xff1a; 【Postgres_Python】使用python脚本批量创建PG数据库 【Postgres_Python】使用python脚本将多个.S…...

消息队列篇--原理篇--RabbitMQ和Kafka对比分析

RabbitMQ和Kafka是两种非常流行的消息队列系统&#xff0c;但它们的设计哲学、架构特点和适用场景存在显著差异。对比如下。 1、架构设计 RabbitMQ&#xff1a; 基AMQP协议&#xff1a;RabbitMQ是基于AMQP&#xff08;高级消息队列协议&#xff09;构建的&#xff0c;支持多…...

俄语画外音的特点

随着全球媒体消费的增加&#xff0c;语音服务呈指数级增长。作为视听翻译和本地化的一个关键方面&#xff0c;画外音在确保来自不同语言和文化背景的观众能够以一种真实和可访问的方式参与内容方面发挥着重要作用。说到俄语&#xff0c;画外音有其独特的特点、挑战和复杂性&…...

【机器学习实战中阶】音乐流派分类-自动化分类不同音乐风格

音乐流派分类 – 自动化分类不同音乐风格 在本教程中,我们将开发一个深度学习项目,用于自动化地从音频文件中分类不同的音乐流派。我们将使用音频文件的频率域和时间域低级特征来分类这些音频文件。 对于这个项目,我们需要一个具有相似大小和相似频率范围的音频曲目数据集…...

Keil5 IDE使用笔记

1 Keil生成bin文件 $K\ARM\ARMCLANG\bin\fromelf.exe --bin --outputL/L.bin !L $K: 表示 Keil 5的安装路径 L: 表示 工程名 !L: 表示 工程名.arf 后缀的文件 可根据实际需要修改 --output 的值调整生成的bin文件的存放路径。 2 下载程序报错 No ST-LINK detected Error: Fla…...

自动化办公|使用Python重命名并移动文件到对应文件夹

在日常的文件管理和处理过程中&#xff0c;我们可能会遇到需要将文件整理到不同文件夹中的需求。例如&#xff0c;我们有一个包含多个文件的目录&#xff0c;文件名的首字符表示文件应该存放在哪个文件夹中。我们可以使用Python脚本来自动完成这个任务&#xff0c;实现文件的分…...

【全栈】SprintBoot+vue3迷你商城(5)

【全栈】SprintBootvue3迷你商城&#xff08;5&#xff09; 上一期我们基本完成了与用户相关的接口&#xff0c;而这些接口都是用户才能干的事情&#xff0c;如果你没登录&#xff0c;那么这些接口功能你都不能实现。 那么如何做到这一步呢&#xff1f; 1.Token 作用 身份…...

Java 并发编程:Java 中的乐观锁与 CAS

大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 025 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自…...

模拟飞行入坑(五) P3D 多通道视角配置 viewgroup

背景&#xff1a; P3D进行多个屏幕显示的时候&#xff0c;如果使用英伟达自带的屏幕融合成一个屏&#xff0c;或者使用P3D单独拉伸窗口&#xff0c;会使得P3D的画面被整体拉伸&#xff0c;又或者,当使用Multichannel进行多个设备联动时&#xff0c;视角同步组合需要配置&#…...

react中hooks之 React 19 新 Hooks useActionState useFormStatus用法总结

React 19 新 Hooks 使用指南: useActionState & useFormStatus 目录 useActionStateuseFormStatus最佳实践 useActionState 概述 useActionState 是 React 19 引入的新 Hook&#xff0c;用于处理表单 action 的状态更新。它允许你基于表单 action 的结果来更新组件状态…...

为AI聊天工具添加一个知识系统 之48 蒙板程序设计(第二版):Respect九宫格【社会形态:治理】

本文要点 1、词汇表Vocabulary &#xff08;普通名词&#xff09; 1) 三组词&#xff08;数据库支持的三个数字散列&#xff09;&#xff1a; 工作&#xff0c;工件&#xff0c;工具。论题&#xff0c;主题词&#xff0c;关键字。口号&#xff0c;符号&#xff0c;编号。 2…...

靠右行驶数学建模分析(2014MCM美赛A题)

笔记 题目 要求分析&#xff1a; 比较规则的性能&#xff0c;分为light和heavy两种情况&#xff0c;性能指的是 a.流量与安全 b. 速度限制等分析左侧驾驶分析智能系统 论文 参考论文 两类规则分析 靠右行驶&#xff08;第一条&#xff09;2. 无限制&#xff08;去掉了第一条…...

6.5、密集波分复用系统(DWDM)/OTN

图中从左到右分为多个部分&#xff0c;分别代表了信号的输入、传输和输出过程。 左侧是客户侧&#xff0c;有普通接口和彩色接口&#xff0c;分别连接到光转发单元&#xff08;OTU&#xff09;。 中间部分是传输线路&#xff0c;包含多个光放大器&#xff08;OBA、OLA、OPA&…...

Unity3D基于Unity整合BEPUphysicsint物理引擎实战详解

引言 Unity3D是一款流行的游戏引擎&#xff0c;提供了丰富的功能和工具&#xff0c;使开发者能够轻松创建各种类型的游戏。其中&#xff0c;帧同步技术是游戏开发中至关重要的一环&#xff0c;它能确保多个玩家在同一时间内看到的游戏状态是一致的。BEPUphysicsint是一个基于U…...

《探秘鸿蒙Next:如何保障AI模型轻量化后多设备协同功能一致》

在鸿蒙Next的多设备协同场景中&#xff0c;确保人工智能模型轻量化后功能的一致性是一项极具挑战性但又至关重要的任务。以下是一些关键的方法和策略。 统一的模型架构与标准 采用标准化框架&#xff1a;选择如TensorFlow Lite、PyTorch Mobile等在鸿蒙Next上适配良好的轻量化…...

微服务知识——4大主流微服务架构方案

文章目录 1、微服务聚合模式2、微服务共享模式3、微服务代理模式4、微服务异步消息模式 微服务是大型架构的必经之路&#xff0c;也是大厂重点考察对象&#xff0c;下面我就重点详解4大主流微服务架构方案。 1、微服务聚合模式 微服务聚合设计模式&#xff0c;解决了如何从多个…...

Java 方法重写

目录 一、什么是方法重写&#xff0c;为什么需要它 二、方法重写的规则 三、方法重写的实际应用场景 四、方法重写与重载的区别 五、总结 在 Java 编程的精彩世界里&#xff0c;方法重写是一项极为重要且实用的特性&#xff0c;它犹如一把神奇的钥匙&#xff0c;为我们开启…...

华为E9000刀箱服务器监控指标解读

美信监控易内置了数千种常见设备监测器&#xff0c;能够监测超过20万项指标。这些指标涵盖了从硬件设备到软件系统&#xff0c;从网络性能到安全状态等各个方面。如下基于美信监控易——IT基础监控模块&#xff0c;对华为E9000刀箱服务器部分监控指标进行解读。 一、华为E9000…...

正则表达式基础与应用

什么是正则表达式&#xff1f; 正则表达式&#xff08;Regular Expression&#xff0c;简称regex&#xff09;是一种用于描述字符串结构的语法规则。它定义了一个搜索模式&#xff0c;可以用来匹配、替换或提取文本中的子串。正则表达式广泛应用于文本处理、数据验证、查找和替…...

微信小程序使用上拉加载onReachBottom。页面拖不动。一直无法触发上拉的事件。

1&#xff0c;可能是原因是你使用了scroll-view的标签&#xff0c;用onReachBottom触发加载事件。这两个是有冲突的。没办法一起使用。如果页面的样式是滚动的是无法去触发页面的onReachBottom的函数的。因此&#xff0c;你使用overflow:auto.来使用页面的某些元素滚动&#xf…...

9. 神经网络(一.神经元模型)

首先&#xff0c;先看一个简化的生物神经元结构&#xff1a; 生物神经元有多种类型&#xff0c;内部也有复杂的结构&#xff0c;但是可以把单个神经元简化为3部分组成&#xff1a; 树突&#xff1a;一个神经元往往有多个树突&#xff0c;用于接收传入的信息。轴突&#xff1a;…...