【JSqlParser】Java使用JSqlParser解析SQL语句总结
简述
Java解析SQL语句有很多工具都可以做到,比如Mybatis、Druid、目前用来用去最全面的仍然是Jsqlparser,它是一个Github上的开源项目,JSqlParser是一个用于解析SQL语句的Java库,它可以帮助开发者分析和操作SQL语句的结构。无论是从事数据库开发、SQL性能优化,还是需要解析SQL语句以进行其他操作,JSqlParser都能提供强大的支持
特点
-
支持多种SQL方言:JSqlParser支持多种数据库的SQL方言,包括MySQL、Oracle、PostgreSQL等,这使得在不同数据库之间进行SQL语句解析变得更加方便。
-
灵活的操作:JSqlParser以多种方式操作SQL语句,例如修改查询条件、提取表名、列名等,甚至整个SQL语句中使用到的函数,从而满足各种需求。
-
易于集成:JSqlParser可以轻松集成到您的Java项目中,只需将其作为依赖项添加到项目中即可。
-
社区支持: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做一个拦截并处理,那么具体可以做哪些骚操作呢
-
SQL语句重写:在某些数据库系统中,为了优化性能或满足特定的需求,可能需要重写SQL语句。通过自定义访问者,可以在AST(abstract syntax tree)层面进行这些操作
-
元数据提取:自定义访问者可以用来提取SQL语句中的元数据,比如查询涉及的所有表名、列名、函数等,这些信息可以用于构建数据库的概要图或进行数据治理。
-
数据屏蔽:在需要对敏感数据进行屏蔽的应用中,可以通过自定义访问者来识别并修改涉及敏感数据的查询,以确保在查询结果中不会暴露敏感信息。
-
动态查询构建:在需要动态构建SQL查询的应用中,可以通过自定义访问者来解析模板SQL语句,并根据实际参数动态替换模板中的占位符,从而构建出完整的SQL语句。
-
缓存策略决策:根据SQL查询的特征,可以通过自定义访问者来判断查询结果是否适合缓存,以及应该使用什么样的缓存策略。
总结
Jsqlparser非常容易上手使用,而它也解决了解析SQL语句的问题,结合Springboot 和 mybatis,可以设计自定义插件,就像Mybatis plus的分页插件那样,可以开发自己系统需求的业务处理功能,方便项目业务的时间,甚至可以拿来提高效率,毕竟总有一些时候,对SQL的解析是绕不开的。
相关文章:

【JSqlParser】Java使用JSqlParser解析SQL语句总结
简述 Java解析SQL语句有很多工具都可以做到,比如Mybatis、Druid、目前用来用去最全面的仍然是Jsqlparser,它是一个Github上的开源项目,JSqlParser是一个用于解析SQL语句的Java库,它可以帮助开发者分析和操作SQL语句的结构。无论是…...
Linux下的dev,sys和proc(TODO)
(TODO) 还有一个sysfs 在 Linux 系统中,/dev、/sys 和 /proc 是三个特殊的虚拟文件系统目录,它们各自有特定的用途,主要用于与设备和内核交互。以下是它们的详细区别和功能说明: 1. /dev(Devi…...

【Unity3D】利用Hinge Joint 2D组件制作绳索效果
目录 一、动态绳索 (可移动根节点) 二、静态绳索 三、利用Skinning Editor(Unity2022.3.15f1正常使用) 四、注意事项 一、动态绳索 (可移动根节点) 动态绳索 DynamicRope空物体 Anchor和whitecircle是相同位置的物体ÿ…...

Springer Nature——Applied Intelligence 投稿指南
投稿系统:Editorial Manager (Manuscript and Peer Review) : 使用Editorial Manager 投稿系统的期刊列表:期刊列表 期刊主页:Spring Nature 主页 投稿主页:Spring Nature Submit SystemSubmission Guidelines: Official Submissi…...

数据结构、数据类型、数字编码、字符编码:保姆级图文详解
文章目录 前言1、数据结构分类1.1、逻辑结构:线性与非线性1.2、物理结构:连续与分散1.3、数据结构的实现方式1.4、数据结构的选择依据 2、基本数据类型2.1、定义与分类2.2、存储形式 3、数字编码3.1、原码、反码与补码3.2、浮点数编码3.3、整数与浮点数区…...

DM适配连接kettle迁移工具(资源库+数据源配置)
适配改造介绍及说明 本次修改基于8.2.0.0版本调整,在该源码基础上进行DM的适配,已支持DM为资源库的配置以及相关数据迁移。kettle资源库是Kettle ETL工具内置的一个资源库。配置好的数据库可以直接缓存到资源库当中;创建、编辑好的转换/作业…...

WINFORM - DevExpress -> alertControl1提示信息框
第一个按钮为常规按钮, 单击触发 ButtonClick 事件. 第二个按钮有选中和未选中状态. 单击触发 ButtonDownChanged 事件。 if (e.ButtonName "alertButton2") { } 在dev用户界面中进行提示(usecontrolwinform) AlertInfo info new AlertInfo("提示",…...

STM32-串口-UART-Asynchronous
一,发送数据 #include "stdio.h" uint8_t hello[]"Hello,blocking\r\n"; HAL_UART_Transmit(&huart1,hello,sizeof(hello),500); 二,MicroLIB-printf(" hello\r\n") #include "stdio.h" #ifdef __GNUC…...

Nginx三种不同类型的虚拟主机(基于域名、IP 和端口)
🏡作者主页:点击! Nginx-从零开始的服务器之旅专栏:点击! 🐧Linux高级管理防护和群集专栏:点击! ⏰️创作时间:2025年1月15日13点14分 目录 1. 基于域名的虚拟主机 …...
Shell控监Kafka积压
1、获取Kafka消息堆积情况 vi check-kafka-lag.sh #!/bin/bashTOPIC"total_random" GROUP_ID"etl-dw" BOOTSTRAP_SERVER"node-01:9092,node-02:9092,node-03:9092"# 检查第一个参数是否为数字 if ! [[ $1 ~ ^[0-9]$ ]]; thenecho &…...

element-ui textarea备注 textarea 多行输入框
发现用这个组件,为了给用户更好的体验,要加下属性 1. 通过设置 autosize 属性可以使得文本域的高度能够根据文本内容自动进行调整,并且 autosize 还可以设定为一个对象,指定最小行数和最大行数。:autosize"{ minRows: 3, ma…...

Transformer创新模型!Transformer+BO-SVR多变量回归预测,添加气泡图、散点密度图(Matlab)
Transformer创新模型!TransformerBO-SVR多变量回归预测,添加气泡图、散点密度图(Matlab) 目录 Transformer创新模型!TransformerBO-SVR多变量回归预测,添加气泡图、散点密度图(Matlab࿰…...

大疆机场及无人机上云
最近基于大疆上云api进行二次开发,后面将按照开发步骤对其进行说明!...

用Cursor生成一个企业官网前端页面(生成腾讯、阿里官网静态页面)
用Cursor生成一个企业官网前端页面 第一版: <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…...
Java 数组排序
目录 1.Java冒泡排序(Bubble Sort) 1.冒泡排序 2.冒泡排序的算法原理 3.冒泡排序的复杂度和性能 4.形成代码 2.Java快速排序(Quick Sort) 3.Java归并排序(Merge Sort) 4.Java选择排序(S…...
LeetCode:78.子集
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:78.子集 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集…...
【经济学通识——国债】
国债 政府的支出一般大于收入,会有赤字 于是会向全社会去借钱。美国债务上限,象征性的(一直上调)全球政府债务都在上升。 亚当斯密觉得市场竞争最有效率 市场自由竞争会不可避免的出现萧条。于是,凯恩斯提出政府调节…...
联合体(Union)
联合体(Union)简介 联合体(union)是 C 和 C 编程语言中的一种数据结构,和结构体(struct)类似,但有一些重要的区别。 定义 联合体中的所有成员共享同一段内存,也就是说…...

Kibana:ES|QL 编辑器简介
作者:来自 Elastic drewdaemon ES|QL 很重要 💪 正如你可能已经听说的那样,ES|QL 是 Elastic 的新查询语言。我们对 ES|QL 寄予厚望。它已经很出色了,但随着时间的推移,它将成为与 Elasticsearch 中的数据交互的最强大…...
【工具】curl工具
curl 官网: https://curl.se/ github: https://github.com/curl?languagec curl 命令 所有参数介绍在线文档 简单使用教程 邮件发送命令 注: 支持SMTP(或者POP3)协议,curl的版本必须高于7.20(含&…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...