JSqlParser的使用

简介

        JSqlParse是一款很精简的sql解析工具,它可以将常用的sql文本解析成具有层级结构的语法树,我们可以针对解析后的节点进行处理(增加、移除、修改等操作),从而生成符合我们业务要求的sql,比如添加过滤条件等等

     JSqlParse采用访问者模式

项目简介

        

项目结构非常简单,从截图上看就5个包。如果对源码感兴趣的可以直接从github上下载源码包调试。其中expression包包含了所有的sql表达式的抽象对象:

statement包含了所有sql语句的类型,比如:增删改查,ddl语句,rollback语句等等

schema包是对数据库基本单元的抽象:服务器、数据库、表、列等等

parser包是整个解析的核心逻辑,感兴趣的可以自行源码调试

使用示例

        上面已经做了关于该解析工具的简单介绍,对于工具类,最重要的使用。以下举例关于增、删、改、查的sql语句中,均增加一列为例介绍该工具的简单使用

依赖引入

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

新增add

原始sql:insert into t_user_info(id,user_name,address) values('123','zhangsan','龙华')

期望在执行该sql时,能增加一列STATUS作为插入

都是一些api的运用,相关代码如下:

package com.lyc.boot.client.test.insert;import com.lyc.boot.client.test.insert.visitor.InsertStatementVisitor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.RowConstructor;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.values.ValuesStatement;import java.util.List;import static com.lyc.boot.client.test.CommonUtil.printStatement;
import static com.lyc.boot.client.test.CommonUtil.printTableName;@Slf4j
public class InsertCommonTest {private static final String INSERT_COMMON = "insert into t_user_info(id,user_name,address) values('123','zhangsan','龙华')";public static void main(String[] args) throws JSQLParserException {useCommonAddColumn();
//        useVisitorAddColumn();}private static void useCommonAddColumn() throws JSQLParserException {Statement statement = CCJSqlParserUtil.parse(INSERT_COMMON);printStatement(statement);if (statement instanceof Insert) {Insert insert = (Insert)statement;printTableName(insert.getTable());List<Column> columns = insert.getColumns();columns.add(new Column("STATUS"));Select select = insert.getSelect();SelectBody selectBody = select.getSelectBody();if (selectBody instanceof SetOperationList) {SetOperationList operationList = (SetOperationList)selectBody;List<SelectBody> selects = operationList.getSelects();for (SelectBody body : selects) {if (body instanceof ValuesStatement) {ValuesStatement valuesStatement = (ValuesStatement)body;ItemsList itemsList = valuesStatement.getExpressions();if(itemsList instanceof ExpressionList) {ExpressionList expressionList = (ExpressionList)itemsList;List<Expression> expressions = expressionList.getExpressions();for (Expression expression : expressions) {if(expression instanceof RowConstructor) {RowConstructor rowConstructor = (RowConstructor)expression;ExpressionList exprList = rowConstructor.getExprList();List<Expression> rowConstructorExList = exprList.getExpressions();rowConstructorExList.add(new StringValue("0"));}}}}}}}printStatement(statement);}/*** 使用访问者方式增加insert的column** @throws JSQLParserException*/private static void useVisitorAddColumn() throws JSQLParserException {Statement statement = CCJSqlParserUtil.parse(INSERT_COMMON);printStatement(statement);statement.accept(new InsertStatementVisitor());printStatement(statement);}}
package com.lyc.boot.client.test.insert.visitor;import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.StatementVisitorAdapter;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;import java.util.List;import static com.lyc.boot.client.test.CommonUtil.printTableName;public class InsertStatementVisitor extends StatementVisitorAdapter {@Overridepublic void visit(Insert insert) {printTableName(insert.getTable());List<Column> columns = insert.getColumns();columns.add(new Column("status"));Select select = insert.getSelect();SelectBody selectBody = select.getSelectBody();selectBody.accept(new InsertSelectVisitor());}
}
package com.lyc.boot.client.test.insert.visitor;import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SelectVisitorAdapter;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.values.ValuesStatement;import java.util.List;public class InsertSelectVisitor extends SelectVisitorAdapter {@Overridepublic void visit(SetOperationList setOpList) {List<SelectBody> selects = setOpList.getSelects();for (SelectBody body : selects) {body.accept(this);}}@Overridepublic void visit(ValuesStatement valuesStatement) {ItemsList itemsList = valuesStatement.getExpressions();itemsList.accept(new InsertItemsListVisitor());}
}
package com.lyc.boot.client.test.insert.visitor;import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.util.validation.validator.ItemsListValidator;import java.util.List;public class InsertItemsListVisitor extends ItemsListValidator {@Overridepublic void visit(ExpressionList expressionList) {List<Expression> expressions = expressionList.getExpressions();for (Expression expression : expressions) {expression.accept(new InsertExpressionVisitor());}}
}
package com.lyc.boot.client.test.insert.visitor;import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.expression.RowConstructor;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;import java.util.List;public class InsertExpressionVisitor extends ExpressionVisitorAdapter {@Overridepublic void visit(RowConstructor rowConstructor) {ExpressionList exprList = rowConstructor.getExprList();List<Expression> expressions = exprList.getExpressions();expressions.add(new StringValue("0"));}
}

      以上是关于新增sql增加一列作为插入的简单运用,其中有通过类型判断处理和通过访问者模式处理(基于java多态实现),最终打印的结果如下:

删除delete

原sql:delete from t_user_info where user_name = ? and addres = ?

期望在删除时增加过滤条件STATUS='0'

相关代码如下:

package com.lyc.boot.client.test.delete;import com.lyc.boot.client.test.delete.visitor.DeleteStatementVisitor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;import static com.lyc.boot.client.test.CommonUtil.printStatement;@Slf4j
public class DeleteCommonTest {private static final String DELETE_COMMON = "delete from t_user_info where user_name = ? and addres = ?";public static void main(String[] args) throws JSQLParserException {
//        commonAddColumn();visitorAddColumn();}private static void visitorAddColumn() throws JSQLParserException{Statement statement = CCJSqlParserUtil.parse(DELETE_COMMON);printStatement(statement);statement.accept(new DeleteStatementVisitor());printStatement(statement);}private static void commonAddColumn() throws JSQLParserException {Statement statement = CCJSqlParserUtil.parse(DELETE_COMMON);printStatement(statement);if(statement instanceof Delete) {Delete delete = (Delete)statement;DeleteStatementVisitor.addColumn(delete);}printStatement(statement);}
}

package com.lyc.boot.client.test.delete.visitor;import com.lyc.boot.client.test.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.StatementVisitorAdapter;
import net.sf.jsqlparser.statement.delete.Delete;import java.util.Objects;@Slf4j
public class DeleteStatementVisitor extends StatementVisitorAdapter {@Overridepublic void visit(Delete delete) {addColumn(delete);}public static void addColumn(Delete delete) {CommonUtil.printTableName(delete.getTable());Expression where = delete.getWhere();Parenthesis parenthesis = new Parenthesis(new EqualsTo(new Column("STATUS"), new StringValue("1")));if (Objects.isNull(where)) {delete.setWhere(parenthesis);} else {delete.setWhere(new AndExpression(where,parenthesis));}}
}

执行结果如下图:

修改update

原sql为:update t_user_info set user_name = ?,address = ? where id = ? and score = ?

期望在修改时set增加STATUS = ? where条件增加STATUS = '1'

package com.lyc.boot.client.test.update;import com.lyc.boot.client.test.update.visitor.UpdateStatementVisitor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.JdbcParameter;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.update.UpdateSet;import java.util.ArrayList;
import java.util.Objects;import static com.lyc.boot.client.test.CommonUtil.printStatement;/*** update语句修改***/
@Slf4j
public class UpdateCommonTest {private static final String COMMON_UPDATE = "update t_user_info set user_name = ?,address = ? where id = ? and score = ?";public static void main(String[] args) throws JSQLParserException {
//        commonUpdateAddColumn();visitorAddColumn();}private static void visitorAddColumn() throws JSQLParserException{Statement statement = CCJSqlParserUtil.parse(COMMON_UPDATE);printStatement(statement);statement.accept(new UpdateStatementVisitor());printStatement(statement);}private static void commonUpdateAddColumn() throws JSQLParserException {Statement statement = CCJSqlParserUtil.parse(COMMON_UPDATE);printStatement(statement);if(statement instanceof Update) {Update update = (Update)statement;Table table = update.getTable();ArrayList<UpdateSet> updateSets = update.getUpdateSets();Column column = new Column("STATUS");StringValue stringValue = new StringValue("?");JdbcParameter jdbcParameter = new JdbcParameter();UpdateSet updateSet = new UpdateSet(column,jdbcParameter);updateSets.add(updateSet);Expression whereExpression = update.getWhere();EqualsTo equalsTo = new EqualsTo(new Column("STATUS"), new StringValue("1"));Parenthesis parenthesis = new Parenthesis(equalsTo);if (Objects.isNull(whereExpression)) {update.setWhere(parenthesis);} else {update.setWhere(new AndExpression(whereExpression,parenthesis));}}printStatement(statement);}}
package com.lyc.boot.client.test.update.visitor;import com.lyc.boot.client.test.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.JdbcParameter;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.StatementVisitorAdapter;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.update.UpdateSet;import java.util.ArrayList;
import java.util.Objects;@Slf4j
public class UpdateStatementVisitor extends StatementVisitorAdapter {@Overridepublic void visit(Update update) {CommonUtil.printTableName(update.getTable());ArrayList<UpdateSet> updateSets = update.getUpdateSets();UpdateSet statusUpdateSet = new UpdateSet(new Column("STATUS"), new JdbcParameter());updateSets.add(statusUpdateSet);Expression where = update.getWhere();Parenthesis parenthesis = new Parenthesis(new EqualsTo(new Column("STATUS"), new StringValue("1")));if (Objects.isNull(where)) {update.setWhere(parenthesis);} else {update.setWhere(new AndExpression(where,parenthesis));}}
}

执行结果如下图所示:

查询select

原sql如下:select id as id,user_name as userName,address as address from t_user_info where id = ? and user_name = ? order by create_time desc

期望在查询时增加where的过滤条件STATUS = '1'

package com.lyc.boot.client.test.select;import com.lyc.boot.client.test.select.visitor.SelectSelectVisitor;
import com.lyc.boot.client.test.select.visitor.SelectStatementVisitor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;import static com.lyc.boot.client.test.CommonUtil.printStatement;@Slf4j
/*** 给查询条件添加更多的过滤条件** and status = '1'*/
public class SelectCommonTest {private static final String SELECT_COMMON = "select id as id,user_name as userName,address as address from t_user_info where id = ? and user_name = ? order by create_time desc";public static void main(String[] args) throws JSQLParserException {
//        commonSelectAddWhere();visitorSelectAddWhere();}private static void visitorSelectAddWhere() throws JSQLParserException{Statement statement = CCJSqlParserUtil.parse(SELECT_COMMON);printStatement(statement);statement.accept(new SelectStatementVisitor());printStatement(statement);}private static void commonSelectAddWhere() throws JSQLParserException {Statement statement = CCJSqlParserUtil.parse(SELECT_COMMON);printStatement(statement);if (statement instanceof Select) {Select select = (Select)statement;SelectBody selectBody = select.getSelectBody();if (selectBody instanceof PlainSelect) {PlainSelect plainSelect = (PlainSelect)selectBody;SelectSelectVisitor.setWhereExpression(plainSelect);}}printStatement(statement);}}
package com.lyc.boot.client.test.select.visitor;import net.sf.jsqlparser.statement.StatementVisitorAdapter;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;public class SelectStatementVisitor extends StatementVisitorAdapter {@Overridepublic void visit(Select select) {SelectBody selectBody = select.getSelectBody();selectBody.accept(new SelectSelectVisitor());}
}
package com.lyc.boot.client.test.select.visitor;import com.lyc.boot.client.test.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectVisitorAdapter;import java.util.List;
import java.util.Objects;@Slf4j
public class SelectSelectVisitor extends SelectVisitorAdapter {@Overridepublic void visit(PlainSelect plainSelect) {setWhereExpression(plainSelect);}public static void setWhereExpression(PlainSelect plainSelect) {Expression where = plainSelect.getWhere();EqualsTo equalsTo = new EqualsTo(new Column("STATUS"), new StringValue("1"));Parenthesis parenthesis = new Parenthesis(equalsTo);if (Objects.isNull(where)) {plainSelect.setWhere(parenthesis);} else {AndExpression andExpression = new AndExpression(where, parenthesis);plainSelect.setWhere(andExpression);}}
}

执行结果如下图:

扩展简析

jsqlParser的实际之一就是在mybaits-plus中的各种插件,比如:多租户插件com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor

该插件的作用是:在执行sql时在where条件处增加了过滤条件(默认是tenant_id = ?,具体的字段可以自己配置实现)

当配置了MybatisPlusInterceptor,并且添加了TenantLineInnerInterceptor时,在执行sql时会被该拦截器拦截,具体的源码流程如下:

当执行查询语句时,sql会被MybatisPlusInterceptor插件拦截,插件调TenantLineInnerInterceptor的beforeQuery方法触发

其中BaseMultiTableInnerInterceptor是JsqlParserSupport的子类,提供了模板方法用于修改sql

图上,生成的sql由com.baomidou.mybatisplus.extension.parser.JsqlParserSupport#parserSingle方法决定

最终执行sql解析完成添加过滤条件的操作:

在TenantLineInnerInterceptor插件中,最终是在where结尾出添加了(默认)tenant_id = xxxx的过滤条件,完成多租户数据隔离处理的。具体的源码逻辑可以调试根据

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全智能深度演进,一键成片让视频创作颠覆式提效

全智能一键成片&#xff0c;让内容创作的「边际成本」逼近于零。 大模型和AIGC技术的发展&#xff0c;可以用“日新月异”来形容&#xff0c;其迭代速度史无前例&#xff0c;涌现出的各类垂直应用模型&#xff0c;也使得音视频行业的应用场景更加广泛和多样化。 然而&#xff…

Linux动态库和静态库的制作和使用

一、什么是动态库跟静态库&#xff1f; 程序函数库分为3种类型&#xff1a;静态函数库(stasic libraries)、共享函数库(shared libraries)、动态加载函数库(dynamically loaded libraries); 1.静态函数库 在程序执行&#xff08;编译&#xff09;前就加入到目标程序中去。 …

如何从零开始拆解uni-app开发的vue项目(三)

前言:前两篇文章我们讲解了如何拆解uni-app开发的项目结构、实现前台数据的动态加载,今天讲一篇如何实现动态加载功能列表,以及美化界面。话不多说,直接先看源码: 在用户成功登录后,会跳转到menu.vue菜单, 再次点击点检功能时,会进入点检的具体功能跳转菜单,我们的点…

在Linux/Debian/Ubuntu上通过 Azure Data Studio 管理 SQL Server 2019

Microsoft 提供 Azure Data Studio&#xff0c;这是一种可在 Linux、macOS 和 Windows 上运行的跨平台数据库工具。 它提供与 SSMS 类似的功能&#xff0c;包括查询、脚本编写和可视化数据。 要在 Ubuntu 上安装 Azure Data Studio&#xff0c;可以按照以下步骤操作&#xff1…

Flink1.18 如何配置算子级别的TTL

1. 解释 从 Flink 1.18 开始&#xff0c;Table API & SQL 支持配置细粒度的状态 TTL 来优化状态使用&#xff0c;可配置粒度为每个状态算子的入边数。具体而言&#xff0c;OneInputStreamOperator 可以配置一个状态的 TTL&#xff0c;而 TwoInputStreamOperator&#xff0…

Sphinx + Readthedocs 避坑速通指南

博主在学习使用 Sphinx 和 Read the docs 的过程中&#xff0c; 碰到了许多奇葩的 bug, 使得很简单的任务花费了很长的时间才解决&#xff0c;现在在这里做一个分享&#xff0c;帮助大家用更少的时间高效上线文档的内容。 总的来说&#xff0c; 任务分为两个部分&#xff1a; …

UE5制作推箱子动作时获取物体与角色朝向的角度及跨蓝图修改变量

就是脑残死磕&#xff0c;你们如果有更好的方法一定要留言啊~~独乐乐不如众乐乐。 做推箱子的时候需要考虑脸是不是面对着箱子&#xff0c;不是必须90度&#xff0c;可以有一个-45~45度的范围。 摸索了一下&#xff0c;有几种做法和几个小白坑&#xff0c;这里列出来。 一、准…

python 创建word并添加文字和统计图

在Python中&#xff0c;可以使用python-docx库来创建Word文档&#xff0c;并添加文本和统计图。以下是一个简单的例子&#xff0c;演示如何创建一个Word文档&#xff0c;添加标题、文本和一个简单的统计图&#xff08;这里以条形图为例&#xff09;。 首先&#xff0c;安装所需…

mysql 索引原理为什么用b+树而不用二叉树

在数据库中&#xff0c;索引是一种数据结构&#xff0c;它能够快速定位到存储在数据库表中特定行的数据。MySQL等数据库管理系统通常使用B树作为索引的数据结构&#xff0c;而不使用二叉树&#xff0c;主要基于以下几个原因&#xff1a; 高度平衡&#xff1a;B树是一种多路搜索…

Ubuntu 下conda创建环境失败报错相关问题

&#xff08;1&#xff09; env |grep -i proxyunset http_proxy unset https_proxy unset no_proxy unset HTTP_PROXY unset HTTPS_PROXY unset NO_PROXY&#xff08;2&#xff09; vim ~/.condarc将里面内容替换为&#xff1a; channels:- https://mirrors.tuna.tsinghua.…

软件推荐 篇三十七:开源免费无广告的在线音乐免费播放 | MusicFree纯净无广告体验-小众冷门推荐

引言 自从QQ音乐没了杰伦、某云开始收费&#xff0c;除了各种广告弹窗导致电脑卡的要死&#xff0c;打工人就靠这点音乐背景熬夜了&#xff0c;木有办法&#xff0c;得有个开源免费的听歌软件吧&#xff0c;一搜github&#xff0c;软件一大堆&#xff0c;作为一个打工仔&#…

【前端寻宝之路】学习和总结HTML表格的实现和合并

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-IWDj0gWiFt6IMq3x {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

LeetCode的LRU缓存实现

LRU是什么意思 LRU是操作系统底层的一个页面置换算法&#xff0c;当空间不够需要换出最长时间没有使用的页面&#xff0c;在本题中的意思就是当到达容量上限的时候要换出最长时间没有被访问过的节点。 如何实现 LRU的实现可以使用链表的方式&#xff0c;参照MySQL的实现&…

GraphPad Prism 10:一站式数据分析解决方案

GraphPad Prism 10是一款功能强大的数据分析和可视化软件&#xff0c;广泛应用于生命科学研究、医学、生物、化学等多个领域。以下是对其详细功能的介绍&#xff1a; 首先&#xff0c;GraphPad Prism 10具有出色的数据可视化功能。它支持各种类型的图表和图形&#xff0c;包括…

解决arco-design下拉框回显id的问题

问题描述 下拉框回显选项中没有的选项&#xff0c;就会出现以下情况&#xff0c;只能把uid回显上去 解决方案 使用ui框架自带的属性fallback-option 用法 按以上操作&#xff0c;即可解决选择框回显uid问题

十一.matplotlib可视化

目录 十一.matplotlib可视化 1 数据可视化 2 应用场景 3 Matplotlib 脚本层 美工层 后端层 3.1 第一个绘图程序 matplotlib常用配置 查看自己电脑上的 字体库 3.2 基本绘图 plt.plot() 3.3 样式和颜色 3.4 画布配置figure 坐标轴及标签 设置图例??? 添加注…

【QT+QGIS跨平台编译】之八十七:【QGIS_Gui跨平台编译】—【qgis_gui.h生成】

文章目录 一、qgis_gui.h介绍二、信息分析三、qgis_gui.h生成一、qgis_gui.h介绍 qgis_gui.h 是 QGIS(Quantum GIS)软件中的一个头文件,主要用于服务于QGIS_GUI库的编译,包含导入、导出宏信息的定义。 二、信息分析 在qgis\src\gui目录,CMakeLists.txt文件记录了相关信息…

python loguru 日志数据代码模块+飞书消息通知

前提&#xff1a; 之前python文件输出日志到文件的时候&#xff0c;使用logging模块&#xff0c;发现特别麻烦&#xff0c;需要文件日期切分&#xff0c;异步打印日志&#xff0c;文件留存时间&#xff0c;这些功能的实现就很麻烦&#xff0c;都需要自己写&#xff0c;之后发现…

Vue3中的reactive与ref

前言&遇到的bug reactive与ref都是Vue3中非常重要的API&#xff0c;众所周知&#xff0c;用这两个API可以创建响应式数据&#xff0c;从而实现Vue的数据驱动视图。 平时用的时候只知道用ref定义基本数据类型&#xff0c;reactive定义引用数据类型。 因为今天发现一个bug…

vue项目的性能优化

结合lighthouse查看各项数据&#xff0c;不断进行性能优化&#xff0c;可以从代码、打包、部署这三个层面来优化 代码层面 1、v-if和v-show区分使用 v-if&#xff08;惰性的&#xff09;用的条件判断&#xff0c;是惰性的&#xff0c;false的话初始不会渲染&#xff0c;适用…