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

Flink 基础 -- 应用开发(Table API SQL) 概念和通用API

1、概述

Apache Flink提供了两个关系API——Table API和SQL——用于统一的流和批处理。Table API是一个用于Java、Scala和Python的语言集成查询API,它允许以非常直观的方式组合来自关系操作符(如选择、过滤和连接)的查询。Flink的SQL支持基于Apache Calcite,它实现了SQL标准。无论输入是连续的(streaming,流)还是有界的(batch,批处理),在两个接口中指定的查询都具有相同的语义并指定相同的结果。

Table API和SQL接口与Flink的DataStream API无缝集成。您可以轻松地在所有api和构建在它们之上的库之间切换。例如,您可以使用MATCH_RECOGNIZE子句从表中检测模式,然后使用DataStream API基于匹配的模式构建警报。

1.1 表程序依赖关系

为了使用Table API和SQL定义数据管道,您需要将Table API作为一个依赖项添加到项目中。

有关如何为Java和Scala配置这些依赖项的更多信息,请参阅项目配置部分。

如果您正在使用Python,请参考Python API的文档

1.2 下一步做什么

  • 概念和公共API:Table API和SQL的共享概念和API。
  • 数据类型:列出预定义的数据类型及其属性。
  • 流式概念:针对表API或SQL的特定于流式的文档,例如时间属性的配置和更新结果的处理。
  • 连接到外部系统:用于向外部系统读写数据的可用连接器和格式。
  • Table API:支持的Table API的操作和API。
  • SQL:支持的SQL操作和语法。
  • 内置函数: 表API和SQL中支持的函数。
  • SQL Client:使用Flink SQL,在没有编程知识的情况下向集群提交一个表程序。
  • SQL网关:一种服务,它允许多个客户端从远程并发地执行SQL。
  • SQL JDBC驱动程序: 向sql-gateway提交SQL语句的JDBC驱动程序。

2、概念和通用API

Table API和SQL集成在一个联合API中。这个API的核心概念是一个表(Table),它作为查询的输入和输出。本文档展示了具有表API和SQL查询的程序的公共结构,如何注册表,如何查询表,以及如何发出(emit)表。

2.1 Table API和SQL程序的结构

下面的代码示例展示了表API和SQL程序的公共结构。

import org.apache.flink.table.api.*;
import org.apache.flink.connector.datagen.table.DataGenConnectorOptions;// Create a TableEnvironment for batch or streaming execution.
// See the "Create a TableEnvironment" section for details.
TableEnvironment tableEnv = TableEnvironment.create(/*…*/);// Create a source table
tableEnv.createTemporaryTable("SourceTable", TableDescriptor.forConnector("datagen").schema(Schema.newBuilder().column("f0", DataTypes.STRING()).build()).option(DataGenConnectorOptions.ROWS_PER_SECOND, 100L).build());// Create a sink table (using SQL DDL)
tableEnv.executeSql("CREATE TEMPORARY TABLE SinkTable WITH ('connector' = 'blackhole') LIKE SourceTable (EXCLUDING OPTIONS) ");// Create a Table object from a Table API query
Table table1 = tableEnv.from("SourceTable");// Create a Table object from a SQL query
Table table2 = tableEnv.sqlQuery("SELECT * FROM SourceTable");// Emit a Table API result Table to a TableSink, same for SQL result
TableResult tableResult = table1.insertInto("SinkTable").execute();

表API和SQL查询可以很容易地集成并嵌入到数据流程序(DataStream programs)中。请查看数据流API集成页面,了解如何将数据流转换为表,反之亦然。

2.2 创建一个表环境(TableEnvironment)

TableEnvironment 是表API和SQL集成的入口点,它负责:

  • Registering a Table in the internal catalog
  • Registering catalogs
  • Loading pluggable modules
  • Executing SQL queries
  • Registering a user-defined (scalar, table, or aggregation) function
  • Converting between DataStream and Table (in case of StreamTableEnvironment)

Table 总是绑定到一个特定的TableEnvironment。不可能在同一个查询中组合不同TableEnvironments中的表(tables),例如,连接或联合它们。TableEnvironment是通过调用静态TableEnvironment.create()方法创建的。

import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.TableEnvironment;EnvironmentSettings settings = EnvironmentSettings.newInstance().inStreamingMode()//.inBatchMode().build();TableEnvironment tEnv = TableEnvironment.create(settings);

或者,用户可以从现有的StreamExecutionEnvironment中创建一个StreamTableEnvironment来与DataStream API进行互操作。

import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);

2.3 在编目Catalog 中创建表

TableEnvironment维护用标识符创建的表的编目映射。每个标识符由3部分组成:编目名、数据库名和对象名。如果未指定编目或数据库,则将使用当前默认值(请参阅表标识符展开部分中的示例)。

表可以是虚表(VIEWS)或常规表(TABLES)。VIEWS可以从现有的Table对象(通常是Table API或SQL查询的结果)创建。TABLES 描述外部数据,如文件、数据库表或消息队列。

2.3.1 临时表与永久表

表可以是临时的,绑定到单个Flink会话的生命周期,也可以是永久的,并且在多个Flink会话和集群中可见。

永久表需要一个编目(catalog)(比如Hive Metastore)来维护表的元数据。永久表创建后,它对连接到编目的任何Flink会话都是可见的,并且将继续存在,直到表被显式删除。

另一方面,临时表总是存储在内存中,并且只在创建它们的Flink会话期间存在。这些表对其他会话是不可见的。它们不绑定到任何编目或数据库,但可以在其中一个名称空间中创建。如果临时表对应的数据库被删除,则临时表不会被删除。

遮蔽(Shadowing)

可以注册具有与现有永久表相同标识符的临时表。临时表会遮蔽永久表,并且只要临时表存在,永久表就无法访问。所有带有该标识符的查询都将对临时表执行。

这可能对实验有用。它允许首先对临时表运行完全相同的查询,例如,只有一个数据子集,或者数据被混淆。一旦验证查询是正确的,就可以针对实际的生产表运行它。

2.3.2 创建表

虚拟表

在SQL术语中,Table API对象对应于VIEW(虚拟表)。它封装了一个逻辑查询计划。它可以在编目(catalog)中创建,如下所示:

// get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// table is the result of a simple projection query 
Table projTable = tableEnv.from("X").select(...);// register the Table projTable as table "projectedTable"
tableEnv.createTemporaryView("projectedTable", projTable);

注意:Table 对象类似于关系数据库系统中的VIEW对象,也就是说,定义表(Table)的查询没有被优化,但是当另一个查询引用注册的表时将被内联。如果多个查询引用同一个已注册表,则每个引用查询将被内联并执行多次,即注册表的结果将不会被共享。

Connector Tables

还可以通过连接器声明创建关系数据库中已知的TABLE。连接器描述了存储表数据的外部系统。存储系统,如Apache Kafka或常规文件系统可以在这里声明。

这样的表可以直接使用表API创建,也可以通过切换到SQL DDL来创建。

// Using table descriptors
final TableDescriptor sourceDescriptor = TableDescriptor.forConnector("datagen").schema(Schema.newBuilder().column("f0", DataTypes.STRING()).build()).option(DataGenConnectorOptions.ROWS_PER_SECOND, 100L).build();tableEnv.createTable("SourceTableA", sourceDescriptor);
tableEnv.createTemporaryTable("SourceTableB", sourceDescriptor);// Using SQL DDL
tableEnv.executeSql("CREATE [TEMPORARY] TABLE MyTable (...) WITH (...)");

2.3.3 扩展表标识符

表总是使用由编目、数据库和表名组成的3部分标识符注册。

用户可以将其中的一个编目和一个数据库设置为“当前编目”和“当前数据库”。有了它们,上面提到的3部分标识符中的前两个部分可以是可选的——如果没有提供它们,将引用当前编目和当前数据库。用户可以通过表API或SQL切换当前编目和当前数据库。

标识符遵循SQL要求,这意味着它们可以用反号字符(`)进行转义。

TableEnvironment tEnv = ...;
tEnv.useCatalog("custom_catalog");
tEnv.useDatabase("custom_database");Table table = ...;// register the view named 'exampleView' in the catalog named 'custom_catalog'
// in the database named 'custom_database' 
tableEnv.createTemporaryView("exampleView", table);// register the view named 'exampleView' in the catalog named 'custom_catalog'
// in the database named 'other_database' 
tableEnv.createTemporaryView("other_database.exampleView", table);// register the view named 'example.View' in the catalog named 'custom_catalog'
// in the database named 'custom_database' 
tableEnv.createTemporaryView("`example.View`", table);// register the view named 'exampleView' in the catalog named 'other_catalog'
// in the database named 'other_database' 
tableEnv.createTemporaryView("other_catalog.other_database.exampleView", table);

2.4 查询表

Table API

Table API是用于Scala和Java的语言集成查询API。与SQL不同,查询没有指定为Strings ,而是在宿主语言中逐步组成。

这个API基于Table类,它表示一个表(流或批处理),并提供应用关系操作的方法。这些方法返回一个新的Table对象,它表示对输入Table应用关系操作的结果。一些关系操作由多个方法调用组成,例如table.groupBy(...).select(),其中groupBy(…)指定表的分组,而select(…)是表分组上的投影。

Table API文档描述了流和批处理表上支持的所有Table API操作。

下面的例子展示了一个简单的Table API聚合查询:

// get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// register Orders table// scan registered Orders table
Table orders = tableEnv.from("Orders");
// compute revenue for all customers from France
Table revenue = orders.filter($("cCountry").isEqual("FRANCE")).groupBy($("cID"), $("cName")).select($("cID"), $("cName"), $("revenue").sum().as("revSum"));// emit or convert Table
// execute query

SQL

Flink的SQL集成基于Apache Calcite,它实现了SQL标准。SQL查询被指定为常规字符串。

SQL文档描述了Flink对流和批处理表的SQL支持。

下面的示例展示了如何指定查询并将结果作为Table返回。

// get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// register Orders table// compute revenue for all customers from France
Table revenue = tableEnv.sqlQuery("SELECT cID, cName, SUM(revenue) AS revSum " +"FROM Orders " +"WHERE cCountry = 'FRANCE' " +"GROUP BY cID, cName");// emit or convert Table
// execute query

下面的示例展示了如何指定将其结果插入到已注册表中的更新查询。

// get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// register "Orders" table
// register "RevenueFrance" output table// compute revenue for all customers from France and emit to "RevenueFrance"
tableEnv.executeSql("INSERT INTO RevenueFrance " +"SELECT cID, cName, SUM(revenue) AS revSum " +"FROM Orders " +"WHERE cCountry = 'FRANCE' " +"GROUP BY cID, cName");

混合表API和SQL

Table API和SQL查询可以很容易地混合,因为两者都返回Table对象:

  • 可以在SQL查询返回的Table对象上定义Table API查询。
  • 通过在TableEnvironment中注册结果表并在SQL查询的FROM子句中引用它,可以根据表API查询的结果定义SQL查询。

2.5 Emit a Table

通过将表写入TableSink来发出(emitted)表。TableSink是一个通用接口,支持多种文件格式(例如CSV, Apache Parquet, Apache Avro),存储系统(例如JDBC, Apache HBase, Apache Cassandra, Elasticsearch),或者消息系统(例如Apache Kafka, RabbitMQ)。

批处理Table 只能写入BatchTableSink,而流Table 需要AppendStreamTableSink, RetractStreamTableSinkUpsertStreamTableSink

请参阅有关表源和接收器的文档,了解有关可用接收器的详细信息以及如何实现自定义DynamicTableSink的说明。

Table.insertInto(String tableName)方法定义了一个完整的端到端管道,将源表发送到已注册的接收表。该方法根据名称从编目中查找表接收器,并验证Table 的模式(Schema)是否与接收器的模式相同。管道可以用TablePipeline.explain()解释,并调用TablePipeline.execute()执行。

下面的例子展示了如何发出(emit)一个Table:

// get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// create an output Table
final Schema schema = Schema.newBuilder().column("a", DataTypes.INT()).column("b", DataTypes.STRING()).column("c", DataTypes.BIGINT()).build();tableEnv.createTemporaryTable("CsvSinkTable", TableDescriptor.forConnector("filesystem").schema(schema).option("path", "/path/to/file").format(FormatDescriptor.forFormat("csv").option("field-delimiter", "|").build()).build());// compute a result Table using Table API operators and/or SQL queries
Table result = ...;// Prepare the insert into pipeline
TablePipeline pipeline = result.insertInto("CsvSinkTable");// Print explain details
pipeline.printExplain();// emit the result Table to the registered TableSink
pipeline.execute();

2.6 翻译并执行查询

表API和SQL查询被转换为数据流程序,无论它们的输入是流还是批处理。查询在内部表示为逻辑查询计划,并分两个阶段进行转换:

  1. Optimization of the logical plan,
  2. Translation into a DataStream program.

表API或SQL查询在以下情况下被转换:

  • 调用TableEnvironment.executeSql()。此方法用于执行给定的语句,并且在调用此方法后立即转换sql查询。
  • TablePipeline.execute()被调用。此方法用于执行源到接收器的管道,并且在调用此方法后立即转换Table API程序。
  • 调用Table.execute()。此方法用于将表内容收集到本地客户机,并且在调用此方法后立即转换table API。
  • 调用StatementSet.execute()TablePipeline(通过StatementSet.add()发送到sink)或INSERT语句(通过StatementSet. addinsertsql()指定)将首先在StatementSet中进行缓冲。一旦StatementSet.execute()被调用,它们就会被转换。所有的汇将被优化为一个DAG。
  • Table 在转换为DataStream 时进行转换(请参阅与数据流的集成)。转换后,它是一个常规的数据流程序,并在调用StreamExecutionEnvironment.execute()时执行。

2.7 查询优化

Apache Flink利用并扩展了Apache Calcite来执行复杂的查询优化。这包括一系列基于规则和成本的优化,例如:

  • Subquery decorrelation based on Apache Calcite
  • Project pruning
  • Partition pruning
  • Filter push-down
  • Sub-plan deduplication to avoid duplicate computation
  • Special subquery rewriting, including two parts:
    • Converts IN and EXISTS into left semi-joins (将IN和EXISTS转换为左半连接)
    • Converts NOT IN and NOT EXISTS into left anti-join (将NOT IN和NOT EXISTS转换为左反连接)
  • Optional join reordering (可选的连接重排序)
    • Enabled via table.optimizer.join-reorder-enabled

注意: IN/EXISTS/NOT IN/NOT EXISTS目前只支持子查询重写的合词条件。

优化器不仅根据计划,还根据数据源提供的丰富统计信息和每个操作的细粒度成本(如io、cpu、网络和内存)做出智能决策。

高级用户可以通过CalciteConfig对象提供自定义优化,该对象可以通过调用TableEnvironment#getConfig#setPlannerConfig提供给表环境。

2.8 Explaining a Table

Table API提供了一种机制来解释用于计算Table的逻辑和优化的查询计划。这是通过Table.explain()方法或StatementSet.explain()方法完成的。Table.explain()返回一个表的计划。StatementSet.explain() 返回多个接收点的计划。它返回一个字符串,描述三个计划:

  • 关系查询的抽象语法树,即未优化的逻辑查询计划、
  • 优化的逻辑查询计划和
  • 物理执行计划。

TableEnvironment.explainSql()TableEnvironment.executeSql()支持执行EXPLAIN语句来获取计划,请参考EXPLAIN页面。

下面的代码显示了一个使用Table.explain()方法的示例和给定表的相应输出:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);DataStream<Tuple2<Integer, String>> stream1 = env.fromElements(new Tuple2<>(1, "hello"));
DataStream<Tuple2<Integer, String>> stream2 = env.fromElements(new Tuple2<>(1, "hello"));// explain Table API
Table table1 = tEnv.fromDataStream(stream1, $("count"), $("word"));
Table table2 = tEnv.fromDataStream(stream2, $("count"), $("word"));
Table table = table1.where($("word").like("F%")).unionAll(table2);System.out.println(table.explain());

上面示例的结果是

== Abstract Syntax Tree ==
LogicalUnion(all=[true])
:- LogicalFilter(condition=[LIKE($1, _UTF-16LE'F%')])
:  +- LogicalTableScan(table=[[Unregistered_DataStream_1]])
+- LogicalTableScan(table=[[Unregistered_DataStream_2]])== Optimized Physical Plan ==
Union(all=[true], union=[count, word])
:- Calc(select=[count, word], where=[LIKE(word, _UTF-16LE'F%')])
:  +- DataStreamScan(table=[[Unregistered_DataStream_1]], fields=[count, word])
+- DataStreamScan(table=[[Unregistered_DataStream_2]], fields=[count, word])== Optimized Execution Plan ==
Union(all=[true], union=[count, word])
:- Calc(select=[count, word], where=[LIKE(word, _UTF-16LE'F%')])
:  +- DataStreamScan(table=[[Unregistered_DataStream_1]], fields=[count, word])
+- DataStreamScan(table=[[Unregistered_DataStream_2]], fields=[count, word])

下面的代码显示了使用StatementSet.explain()方法的多汇计划的示例和相应的输出:

EnvironmentSettings settings = EnvironmentSettings.inStreamingMode();
TableEnvironment tEnv = TableEnvironment.create(settings);final Schema schema = Schema.newBuilder().column("count", DataTypes.INT()).column("word", DataTypes.STRING()).build();tEnv.createTemporaryTable("MySource1", TableDescriptor.forConnector("filesystem").schema(schema).option("path", "/source/path1").format("csv").build());
tEnv.createTemporaryTable("MySource2", TableDescriptor.forConnector("filesystem").schema(schema).option("path", "/source/path2").format("csv").build());
tEnv.createTemporaryTable("MySink1", TableDescriptor.forConnector("filesystem").schema(schema).option("path", "/sink/path1").format("csv").build());
tEnv.createTemporaryTable("MySink2", TableDescriptor.forConnector("filesystem").schema(schema).option("path", "/sink/path2").format("csv").build());StatementSet stmtSet = tEnv.createStatementSet();Table table1 = tEnv.from("MySource1").where($("word").like("F%"));
stmtSet.add(table1.insertInto("MySink1"));Table table2 = table1.unionAll(tEnv.from("MySource2"));
stmtSet.add(table2.insertInto("MySink2"));String explanation = stmtSet.explain();
System.out.println(explanation);

the result of multiple-sinks plan is

== Abstract Syntax Tree ==
LogicalLegacySink(name=[`default_catalog`.`default_database`.`MySink1`], fields=[count, word])
+- LogicalFilter(condition=[LIKE($1, _UTF-16LE'F%')])+- LogicalTableScan(table=[[default_catalog, default_database, MySource1, source: [CsvTableSource(read fields: count, word)]]])LogicalLegacySink(name=[`default_catalog`.`default_database`.`MySink2`], fields=[count, word])
+- LogicalUnion(all=[true]):- LogicalFilter(condition=[LIKE($1, _UTF-16LE'F%')]):  +- LogicalTableScan(table=[[default_catalog, default_database, MySource1, source: [CsvTableSource(read fields: count, word)]]])+- LogicalTableScan(table=[[default_catalog, default_database, MySource2, source: [CsvTableSource(read fields: count, word)]]])== Optimized Physical Plan ==
LegacySink(name=[`default_catalog`.`default_database`.`MySink1`], fields=[count, word])
+- Calc(select=[count, word], where=[LIKE(word, _UTF-16LE'F%')])+- LegacyTableSourceScan(table=[[default_catalog, default_database, MySource1, source: [CsvTableSource(read fields: count, word)]]], fields=[count, word])LegacySink(name=[`default_catalog`.`default_database`.`MySink2`], fields=[count, word])
+- Union(all=[true], union=[count, word]):- Calc(select=[count, word], where=[LIKE(word, _UTF-16LE'F%')]):  +- LegacyTableSourceScan(table=[[default_catalog, default_database, MySource1, source: [CsvTableSource(read fields: count, word)]]], fields=[count, word])+- LegacyTableSourceScan(table=[[default_catalog, default_database, MySource2, source: [CsvTableSource(read fields: count, word)]]], fields=[count, word])== Optimized Execution Plan ==
Calc(select=[count, word], where=[LIKE(word, _UTF-16LE'F%')])(reuse_id=[1])
+- LegacyTableSourceScan(table=[[default_catalog, default_database, MySource1, source: [CsvTableSource(read fields: count, word)]]], fields=[count, word])LegacySink(name=[`default_catalog`.`default_database`.`MySink1`], fields=[count, word])
+- Reused(reference_id=[1])LegacySink(name=[`default_catalog`.`default_database`.`MySink2`], fields=[count, word])
+- Union(all=[true], union=[count, word]):- Reused(reference_id=[1])+- LegacyTableSourceScan(table=[[default_catalog, default_database, MySource2, source: [CsvTableSource(read fields: count, word)]]], fields=[count, word])

相关文章:

Flink 基础 -- 应用开发(Table API SQL) 概念和通用API

1、概述 Apache Flink提供了两个关系API——Table API和SQL——用于统一的流和批处理。Table API是一个用于Java、Scala和Python的语言集成查询API&#xff0c;它允许以非常直观的方式组合来自关系操作符(如选择、过滤和连接)的查询。Flink的SQL支持基于Apache Calcite&#x…...

Flink之Java Table API的使用

Java Table API的使用 使用Java Table API开发添加依赖创建表环境创建表查询表输出表使用示例 表和流的转换流DataStream转换成表Table表Table转换成流DataStream示例数据类型 自定义函数UDF标量函数表函数聚合函数表聚合函数 API方法汇总基本方法列操作聚合操作Joins合并操作排…...

【Unity细节】Unity中如何让组件失活而不是物体失活

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…...

[设计模式] 建造者模式

一、引言 起因是学习okhttp过程中遇到的这段代码 Request request original.newBuilder().url(original.url()).header("Authorization", "Bearer " BearerTokenUtils.getToken(configuration.getApiKey(), configuration.getApiSecret())).header(&quo…...

在DDD领域驱动下的微服务数据库的MVC设计思路(高度可行性)

在DDD领域驱动下的微服务架构中使用MVC设计思路来设计数据库是可行的&#xff0c;因为MVC是一种经典的软件架构模式&#xff0c;可以将应用程序分为三个主要部分&#xff1a;模型、视图和控制器。在微服务架构中&#xff0c;每个微服务可以看作是一个模块&#xff0c;可以使用M…...

Leetcode2834. 找出美丽数组的最小和

Every day a Leetcode 题目来源&#xff1a;2834. 找出美丽数组的最小和 解法1&#xff1a;贪心 从最小正整数 1 开始枚举&#xff0c;设当前数为 num&#xff0c;如果 nums 里没有 target - num&#xff0c;就说明可以添加 num&#xff0c;依次填满直到有 n 个数即可。 用…...

acwing算法基础之搜索与图论--kruskal算法

目录 1 基础知识2 模板3 工程化 1 基础知识 kruskal算法的关键步骤为&#xff1a; 将所有边按照权重从小到大排序。定义集合S&#xff0c;表示生成树。枚举每条边(a,b,c)&#xff0c;起点a&#xff0c;终点b&#xff0c;边长c。如果结点a和结点b不连通&#xff08;用并查集来…...

微信H5跳转微信小程序

官方文档&#xff1a;目录 | 微信开放文档 方法一&#xff1a;微信浏览器打开的h5跳转方式 HTML代码 <wx-open-launch-weapp id"launch-btn" username"所需跳转的小程序原始id" path"pages/pay/pay"><script type"text/wxtag…...

Yii2 引入 外部无命名空间的类,Class not found

记一次问题解决 问题描述 支付宝开放平台SDK v2 无命名空间。需 require 引入。 require Yii::$app->vendorPath."/alipay-sdk-php/v2/aop/AopClient.php"; var_dump(new AopClient([]));exit();上述写法会直接报错。 Class temporary\controllers\AopClient …...

设计模式是测试模式咩?

设计模式和测试模式概述 软件的生命周期为什么要进行测试&#xff08;测试的目的&#xff09;&#xff1f;软件的设计模式1. **瀑布模型**3. 增量和迭代模型4. 敏捷模型5. 喷泉模型 测试模型V模型W模型 一个应用程序从出生到“死亡”会经过非常漫长的流程…… 软件的生命周期 …...

Aspose.OCR for .NET 2023Crack

Aspose.OCR for .NET 2023Crack 为.NET在图片上播放OCR使所有用户和程序员都可以从特定的图像片段中提取文本和相关的细节&#xff0c;如字体、设计以及书写位置。这一特定属性为OCR的性能及其在扫描遵循排列的记录时的功能提供了动力。OCR的库使用一条线甚至几条线来处理这些特…...

conda环境中pytorch1.2.0版本安装包安装一直失败解决办法!!!

conda环境中pytorch1.2.0版本安装包安装一直失败解决办法 cuda10.0以及cudnn7.4现在以及安装完成&#xff0c;就差torch的安装了&#xff0c;现在torch我要装的是1.2.0版本的&#xff0c;安装包以及下载好了&#xff0c;安装包都是在这个网站里下载的&#xff08;点此进入&…...

后端面试问题(学习版)

JAVA相关 JAVA语言概述 1. 一个".java"源文件中是否可以包含多个类&#xff1f;有什么限制&#xff1f; 可以。 一个源文件可以声明多个类&#xff0c;但是最多只能有一个类使用public进行声明 且要求声明public的类的类名与源文件相同。 2. Java的优势&#xff…...

数据管理系统-week1-介绍

文章目录 一、数据它是什么&#xff1f;二、电子存储设备三、持久存储设备1、硬盘驱动器&#xff08;HDD&#xff09;、硬盘、硬盘驱动器是一种用于存储和检索数字信息的机电持久存储设备。2、固态硬盘&#xff08;SSD&#xff09;使用非易失性存储器&#xff0c;即使用NAND闪存…...

【SpringBoot】手写模拟SpringBoot核心流程

依赖包 新建一个工程&#xff0c;包含两个 module&#xff1a; springboot 模块&#xff0c;表示 springboot 源码实现&#xff1b;user 模块&#xff0c;表示业务系统&#xff0c;使用 springboot 模块&#xff1b; 依赖包&#xff1a;Spring、SpringMVC、Tomcat 等&#xff…...

应对.locked勒索病毒:恢复、预防全方位攻略

导言&#xff1a; .locked勒索病毒并非简单的数字威胁&#xff0c;它是一场对个人和企业数字资产的精密审判。这种病毒通过各种方式感染系统&#xff0c;从而以瞬间之间将用户的关键文件变成数字拼图&#xff0c;无情地要求赎金以换取解锁的密钥。如果您正在经历勒索病毒数据恢…...

基于DS1302时钟液晶12864显示2路闹钟仿真及源程序

一、系统方案 1、本设计采用51单片机作为主控器。 2、DS1302采集年月日时分秒送到液晶12864显示。 3、按键年月日时分秒&#xff0c;两路闹钟。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 uchar clock_time[6] {0X00,0X59,0X23,0X09,0X…...

AGC034E Complete Compress

AGC034E Complete Compress 洛谷[AGC034E] Complete Compress 题目大意 给你一棵有 n n n个节点的树&#xff0c;并用 01 01 01串告诉你哪些节点上有棋子&#xff08;恰好一棵&#xff09;。 你可以进行若干次操作&#xff0c;每次操作可以将两颗距离至少为 2 2 2的棋子向彼…...

python设计模式12:状态模式

什么是状态机&#xff1f; 关键属性&#xff1a; 状态和转换 状态&#xff1a; 系统当前状态 转换&#xff1a;一种状态到另外一种状态的变化。 转换由触发事件或是条件启动。 状态机-状态图 状态机使用场景&#xff1a; 自动售货机 电梯 交通灯 组合锁 停车计时…...

JS对图片尺寸和DPI进行编辑修改(1寸照修改为2寸照)

各种报名都对照片有大小限制&#xff0c;鉴于这种情况&#xff0c;网上搜了后拼凑出了如下代码&#xff0c;用于解决1寸照片修改为2寸照片&#xff0c;同时将DPI修改为300&#xff0c;当然也可以根据自己的情况修改代码&#xff1a; HTML <input type"file" id&…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...