【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 SQL
 Statement 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 {
    @Override
    public 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的解析是绕不开的。

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

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

相关文章

Ubuntu本地部署网站

目录 1.介绍 2.安装apache 3.网页升级 1.介绍 网站其实就相当于一个文件夹&#xff0c;用域名访问一个网页&#xff0c;就相当于访问了一台电脑的某一个文件夹&#xff0c;在网页中看见的视频&#xff0c;视频和音乐其实就是文件夹里面的文件。为什么网页看起来不像电脑文件夹…

C++异常处理详解

概述 这篇博客将深入探讨 C异常处理的工作原理, 最佳实践以及如何编写异常安全的代码, 配有代码示例和详细说明. 1. 异常的挑战 性能开销: 异常在失败情况下会带来显著的运行时成本. 图片来自: Introduction to proposed std::expected - Niall Douglas - Meeting C 2017 推…

零基础构建最简单的 Tauri2.0 桌面项目 Star 88.4k!!!

目录 预安装环境 安装nodejs windows下安装 linux下安装 nodejs常遇问题 安装C环境 介绍 下载 安装 安装Rust语言 Tauri官网 安装 vscode 安装 rust 插件 安装 Tauri 插件 运行成果 预安装环境 安装nodejs windows下安装 NodeJs_安装及下载_哔哩哔哩_bilibi…

Python基于Django的图像去雾算法研究和系统实现(附源码,文档说明)

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

【AI插件】在VS Code中免费使用GitHub Copilot

什么是GitHub Copilot GitHub Copilot 是由 GitHub 和 OpenAI 合作开发的一款 AI 编程助手&#xff0c;旨在帮助开发者提高编程效率。它通过分析大量的开源代码&#xff0c;生成上下文相关的代码建议和自动补全&#xff0c;支持多种编程语言和框架。GitHub Copilot 是基于 Ope…

map和set c++

关联式容器也是⽤来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;关联式容器逻辑结构通常是⾮线性结构&#xff0c;两个位置有紧密的关联关系&#xff0c;交换⼀下&#xff0c;他的存储结构就被破坏了。顺序容器中的元素是按关键字来保存和访问的。关联式容器有map/…

2025年01月蓝桥杯Scratch1月stema选拔赛真题—美丽的图形

美丽的图形 编程实现美丽的图形具体要求: 1)点击绿旗&#xff0c;角色在舞台中心&#xff0c;如图所示&#xff1b; 2)1秒后&#xff0c;绘制一个边长为 140的红色大正方形&#xff0c;线条粗细为 3&#xff0c;正方形的中心为舞台中心&#xff0c;如图所示; 完整题目可点击下…

hive连接mysql报错:Unknown version specified for initialization: 3.1.0

分享下一些报错的可能原因吧 1.要开启hadoop 命令&#xff1a;start-all.sh 2.检查 hive-site.xml 和 hive-env.sh。 hive-site.xml中应设置自己mysql的用户名和密码 我的hive-site.xml如下&#xff1a; <configuration><property><name>javax.jdo.opt…

AI编程工具使用技巧——通义灵码

活动介绍通义灵码1. 理解通义灵码的基本概念示例代码生成 2. 使用明确的描述示例代码生成 3. 巧妙使用注释示例代码生成 4. 注意迭代与反馈原始代码反馈后生成优化代码 5. 结合生成的代码进行调试示例测试代码 其他功能定期优化生成的代码合作与分享结合其他工具 总结 活动介绍…

Python编程与在线医疗平台数据挖掘与数据应用交互性研究

一、引言 1.1 研究背景与意义 在互联网技术飞速发展的当下,在线医疗平台如雨后春笋般涌现,为人们的就医方式带来了重大变革。这些平台打破了传统医疗服务在时间和空间上的限制,使患者能够更加便捷地获取医疗资源。据相关报告显示,中国基于互联网的医疗保健行业已进入新的…

大文件上传服务-后端V1V2

文章目录 大文件上传概述:minio分布式文件存储使用的一些技术校验MD5的逻辑 uploadV1 版本 1uploadv2 版本 2 大文件上传概述: 之前项目做了一个文件上传的功能,最近看到有面试会具体的问这个上传功能的细节&#xff0c;把之前做的项目拿过来总结一下&#xff0c;自己写的一个…

[BrainShadow-V1] VR头戴设备统计报告

Brain-Shadow-V1 EventVR headsetsReported byXiao enDate2025/01/15Version1.0 HTC Vive Pro 2 Pro HTC Vive Pro 2 是一款高端虚拟现实头显&#xff0c;配备双 2.5K 显示屏&#xff0c;组合分辨率达到 48962448&#xff0c;提供 120 的视场角和 120Hz 的刷新率。该设备支持…

路由环路的产生原因与解决方法(1)

路由环路 路由环路就是数据包不断在这个网络传输&#xff0c;始终到达不了目的地&#xff0c;导致掉线或者网络瘫痪。 TTL &#xff08;生存时间&#xff09;&#xff1a;数据包每经过一个路由器的转发&#xff0c;其数值减1&#xff0c;当一个数据包的TTL值为0是&#xff0c;路…

工业网口相机:如何通过调整网口参数设置,优化图像传输和网络性能,达到最大帧率

项目场景 工业相机是常用与工业视觉领域的常用专业视觉核心部件&#xff0c;拥有多种属性&#xff0c;是机器视觉系统中的核心部件&#xff0c;具有不可替代的重要功能。 工业相机已经被广泛应用于工业生产线在线检测、智能交通,机器视觉,科研,军事科学,航天航空等众多领域 …

企业邮箱iRedMail搭建

用自己的域名作为邮箱的后缀&#xff0c;好看、有面子&#xff01;只要域名不过期&#xff0c;那么&#xff0c;你的邮箱就永远存在&#xff01; 邮件系统很多&#xff0c;宝塔自带的邮局更是简单&#xff0c;但是若想邮箱可靠&#xff08;丢邮件、发送邮件进入对方垃圾箱等&a…

Redis的安装和配置、基本命令

一、实验目的 本实验旨在帮助学生熟悉Redis的安装、配置和基本使用&#xff0c;包括启动Redis服务、使用命令行客户端进行操作、配置Redis、进行多数据库操作以及掌握键值相关和服务器相关的命令。 二、实验环境准备 1. JAVA环境准备&#xff1a;确保Java Development Kit …

mysql community server社区版M2 macbook快速安装

Django玩的时候用到了mysql&#xff0c;简单整理一下这个老伙计的安装教程 1. 下载地址&#xff1a;MySQL :: Download MySQL Community Server 2. M2芯片mac的话选择第一个下载&#xff0c;按提示安装即可 3. 或者直接用这篇文章附属安装包 4. 但安装之后可能会有zsh: command…

【桌面程序】PyWebview跨平台桌面应用程序

什么是PyWebview PyWebView 是一个轻量级的 Python 库&#xff0c;用于将网页&#xff08;HTML、CSS、JavaScript&#xff09;嵌入到本地应用程序的窗口中。它允许你创建带有图形用户界面&#xff08;GUI&#xff09;的桌面应用程序&#xff0c;并且能够使用 Web 技术&#xf…

迅为RK3568开发板篇OpenHarmony实操HDF驱动控制LED-编写内核 LED HDF 驱动程序

接下来编译 LED 驱动&#xff0c;该驱动用于在基于华为设备框架&#xff08;HDF&#xff09;的系统中控制 LED 灯的开关&#xff0c;完整代码如下所示&#xff1a; 更多内容可以关注&#xff1a;迅为RK3568开发板篇OpenHarmony

【tailscale 和 ssh】当服务器建立好节点,但通过客户端无法通过 ssh 连接

背景 当服务器建立好节点&#xff0c;一切显示正常但通过客户端无法通过 vs code 中的 ssh 连接到服务器 问题解决 因为服务器是重装过的&#xff0c;所以忘记在服务器上下载 ssh 了。。。安装完成并启动 SSH 服务后便可正常连接&#xff01; sudo apt update sudo apt in…