Flutter——数据库Drift开发详细教程(七)

目录

  • 入门
    • 设置
  • 漂移文件
    • 入门
    • 变量
    • 数组
    • 定义表
      • 支持的列类型
      • 漂移特有的功能
  • 导入
  • 嵌套结果
  • LIST子查询
  • Dart 互操作
    • SQL 中的 Dart 组件
    • 类型转换器
    • 现有的行类
    • Dart 文档注释
  • 结果类名称
  • 支持的语句

入门

Drift 提供了一个dart_api来定义表和编写 SQL 查询。尤其当您已经熟悉 SQL 时,直接在 SQL 中使用CREATE TABLE语句定义表可能会更方便。得益于 Drift 内置的强大 SQL 解析器和分析器,您仍然可以运行类型安全的 SQL 查询,并支持自动更新流和所有其他 Drift 功能。SQL 的有效性在构建时进行检查,Drift 会为每个表和 SQL 语句生成匹配的方法。

设置

添加漂移依赖项的基本设置与 dart_apis 的设置一致。具体描述请参阅设置页面。

不同之处在于表和查询的声明方式。为了让Drift识别SQL,需要将其放入.drift文件中。在此示例中,我们.drift在数据库类旁边使用了一个名为的文件tables.drift:

-- this is the tables.drift file
CREATE TABLE todos (id INT NOT NULL PRIMARY KEY AUTOINCREMENT,title TEXT,body TEXT,category INT REFERENCES categories (id)
);CREATE TABLE categories (id INT NOT NULL PRIMARY KEY AUTOINCREMENT,description TEXT
) AS Category; -- see the explanation on "AS Category" below/* after declaring your tables, you can put queries in here. Justwrite the name of the query, a colon (:) and the SQL: */
todosInCategory: SELECT * FROM todos WHERE category = ?;/* Here's a more complex query: It counts the amount of entries per
category, including those entries which aren't in any category at all. */
countEntries:SELECTc.description,(SELECT COUNT(*) FROM todos WHERE category = c.id) AS amountFROM categories cUNION ALLSELECT null, (SELECT COUNT(*) FROM todos WHERE category IS NULL);

Drift 会为您的表生成 Dart 类,这些类的名称基于表名。默认情况下,Drift 会去除s表尾的空格。这在大多数情况下都适用,但在某些情况下(例如categories上表)则不行。我们希望生成一个 Category类(而不是Categorie),所以我们告诉 Drift 生成一个不同的名称,并AS 在末尾添加声明。

将漂移文件集成到数据库很简单,只需将其添加到 注释include的参数中即可**@DriftDatabase**。tables这里可以省略该参数,因为没有 Dart 定义的表需要添加到数据库中。

import 'dart:io';import 'package:drift/drift.dart';
// These imports are used to open the database
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;part 'database.g.dart';(// relative import for the drift file. Drift also supports `package:`// importsinclude: {'tables.drift'},
)
class AppDb extends _$AppDb {AppDb() : super(_openConnection());int get schemaVersion => 1;
}LazyDatabase _openConnection() {// the LazyDatabase util lets us find the right location for the file async.return LazyDatabase(() async {// put the database file, called db.sqlite here, into the documents folder// for your app.final dbFolder = await getApplicationDocumentsDirectory();final file = File(p.join(dbFolder.path, 'db.sqlite'));return NativeDatabase.createInBackground(file);});
}

要生成database.g.dart包含**_$AppDb** 超类的文件,请dart run build_runner build在命令行上运行。

漂移文件

Drift 文件是一项新功能,允许您使用 SQL 编写所有数据库代码。但与您传递给简单数据库客户端的原始 SQL 字符串不同,Drift
文件中的所有内容都经过 Drift 强大的 SQL 分析器验证。这使您能够更安全地编写 SQL 查询:Drift
会在构建过程中发现其中的错误,并为其生成类型安全的 dart_api,这样您就无需手动读取结果。

入门

要使用此功能,我们需要创建两个文件:database.dart和tables.drift。 Dart 文件仅包含设置数据库的最少代码:

import 'package:drift/drift.dart';
import 'package:drift/native.dart';part 'database.g.dart';(include: {'tables.drift'},
)
class MyDb extends _$MyDb {// This example creates a simple in-memory database (without actual// persistence).// To store data, see the database setups from other "Getting started" guides.MyDb() : super(NativeDatabase.memory());int get schemaVersion => 1;
}

我们现在可以在漂移文件中声明表和查询:

CREATE TABLE todos (id INT NOT NULL PRIMARY KEY AUTOINCREMENT,title TEXT NOT NULL,content TEXT NOT NULL,category INTEGER REFERENCES categories(id)
);CREATE TABLE categories (id INT NOT NULL PRIMARY KEY AUTOINCREMENT,description TEXT NOT NULL
) AS Category; -- the AS xyz after the table defines the data class name-- You can also create an index or triggers with drift files
CREATE INDEX categories_description ON categories(description);-- we can put named SQL queries in here as well:
createEntry: INSERT INTO todos (title, content) VALUES (:title, :content);
deleteById: DELETE FROM todos WHERE id = :id;
allTodos: SELECT * FROM todos;

使用 运行构建运行器后dart run build_runner build,drift 将写入database.g.dart 包含_$MyDb超类的文件。让我们看看我们得到了什么:

  • 生成的数据类(Todo和Category)以及用于插入的配套版本(更多信息请参阅Dart Interop)。默认情况下,drift
    会从类的表名中去掉尾部的“s”。这就是为什么我们AS Category在第二个表上使用 —— 否则它就会被 Categorie这样调用。
  • 运行查询的方法:
  • 一个Future createEntry(String title, String
    content)方法。它使用提供的数据创建一个新的待办事项条目,并返回所创建条目的 ID。
  • Future deleteById(int id):根据 ID 删除待办事项条目,并返回受影响的行数。
  • Selectable
    allTodos()。它可用于获取或查看所有待办事项。它可以与allTodos().get()和 一起
    使用allTodos().watch()。
  • 不匹配表的选择语句的类。在上面的例子中,该类AllTodosResult包含所有字段 todos以及相关类别的描述。

变量

在命名查询中,您可以像在 SQL 中一样使用变量。我们支持常规变量 ( ?)、显式索引变量 ( ?123) 和冒号命名变量 ( :id)。我们不支持使用 @ 或 $ 声明的变量。编译器将尝试通过查看变量的上下文来推断其类型。这使得 Drift 能够为您的查询生成类型安全的 API,变量将作为参数写入您的方法。

当变量类型不明确时,分析器可能无法解析该变量的类型。对于这些情况,你也可以指定变量的显式类型:

myQuery(:variable AS TEXT): SELECT :variable;

除了基类型之外,还可以声明类型可为空:

myNullableQuery(:variable AS TEXT OR NULL): SELECT :variable;

最后,你可以在 Dart 中使用命名参数时声明一个变量是必需的。为此,请添加一个REQUIRED关键字:

myRequiredQuery(REQUIRED :variable AS TEXT OR NULL): SELECT :variable;

named_parameters 请注意,这仅在启用构建选项时才有效。此外,默认情况下需要非空变量。

数组

如果要检查某个值是否在值数组中,可以使用IN ?。这不是有效的 SQL,但 Drift 会在运行时对其进行语法糖解析。因此,对于以下查询:

entriesWithId: SELECT * FROM todos WHERE id IN ?;

Drift 会生成一个Selectable entriesWithId(List ids)方法。运行后entriesWithId([1,2])会生成SELECT * … id IN (?1, ?2)并绑定相应的参数。为了确保其按预期工作,Drift 施加了两个小限制:

  1. 没有显式变量:WHERE id IN ?2将在构建时被拒绝。由于变量已扩展,因此为其提供单个索引是无效的。
  2. 变量后没有更高的显式索引:运行 WHERE id IN ? OR title = ?2也会被拒绝。扩展变量可能会与显式索引冲突,这就是
    Drift 禁止这样做的原因。当然,它id IN ? OR title = ?会按预期工作。

定义表

在.drift文件中,您可以使用CREATE TABLE语句定义表,就像在 SQL 中编写一样。

支持的列类型

就像 sqlite 本身一样,我们使用此算法 根据声明的类型名称来确定列类型。

此外,类型名为BOOLEAN或DATETIME的 列,其 Dart 对应类型为bool或DateTime。布尔值存储为INTEGER(0或1)。日期时间存储为 unix 时间戳(INTEGER)或 ISO-8601 时间戳(TEXT),具体取决于可配置的构建选项。对于在 Dart 中应表示为 的整数BigInt(即,为了在编译为 JS 时更好地兼容大数),请使用 类型定义列INT64。

ENUM()Dart 枚举可以通过使用引用 Dart 枚举类的类型自动按其索引进行存储:

enum Status {none,running,stopped,paused
}import 'status.dart';CREATE TABLE tasks (id INTEGER NOT NULL PRIMARY KEY,status ENUM(Status)
);

有关存储枚举的更多信息,请参阅类型转换器页面。除了使用按索引映射枚举的整数之外,您还可以按名称存储它们。为此,请使用ENUMNAME(…)而不是ENUM(…)。

有关所有支持类型的详细信息,以及如何在日期时间模式之间切换的信息,请参阅本节。

表达式中还支持其他特定于漂移的类型(BOOLEAN、和) DATETIME, 这对于视图很有帮助:ENUMENUMNAMECAST

CREATE VIEW with_next_status ASSELECT id, CAST(status + 1 AS ENUM(Status)) AS statusFROM tasksWHERE status < 3;

漂移特有的功能

为了支持 Drift 的 dart_api,CREATE TABLEDrift 文件中的语句可以使用 Dart 特有功能的特殊语法。当然,Drift 会CREATE TABLE 在运行语句之前删除这些特殊语法。

  • 您可以通过附加到语句来为表或定义的查询定义自定义行类。WITH YourDartClassCREATE TABLE
  • 或者,您可以使用AS DesiredRowClassName来更改由漂移生成的行类的名称。
  • 自定义行类和自定义表名也适用于视图,例如 CREATE VIEW my_view AS DartName AS SELECT …;。
  • 在列定义中,MAPPED BY可用于将转换器应用于 该列。
  • 类似地,JSON KEY可以使用约束来定义在将该表的一行序列化为 JSON 时将使用的键漂移。
  • 最后,AS getterName可以用作列约束来覆盖 Dart 中该列的生成名称。当 SQL
    中该列的名称所启发的默认列名与生成的表类的其他成员冲突时,此功能非常有用。

导入

您可以将导入语句放在文件顶部drift:

import 'tables.drift'; -- single quotes are required for imports

所有可从其他文件访问的表也将在当前文件及其数据库中可见includes。如果您想对另一个漂移文件中定义的表声明查询,则还需要导入该文件以使这些表可见。请注意,漂移文件中的导入始终具有传递性,因此在上面的示例中,您也将拥有所有在可用文件中声明的导入。漂移文件other.drift没有机制。export

您也可以将 Dart 文件导入到漂移文件中,这样,所有通过 Dart 表声明的表都可以在查询中使用。我们支持相对导入和package:您熟悉的 Dart 导入。

嵌套结果

许多查询通常使用 SELECT table.*以下语法来获取某个表的所有列。当应用于来自连接的多个表时,这种方法可能会变得有点繁琐,如下例所示:

CREATE TABLE coordinates (id INTEGER NOT NULL PRIMARY KEY,lat REAL NOT NULL,long REAL NOT NULL
);CREATE TABLE saved_routes (id INTEGER NOT NULL PRIMARY KEY,name TEXT NOT NULL,"from" INTEGER NOT NULL REFERENCES coordinates (id),"to" INTEGER NOT NULL REFERENCES coordinates (id)
);routesWithPoints: SELECT r.id, r.name, f.*, t.* FROM saved_routes rINNER JOIN coordinates f ON f.id = r."from"INNER JOIN coordinates t ON t.id = r."to";

为了匹配返回的列名,同时避免 Dart 中的名称冲突,drift 会生成一个包含、 、id、name和 一个字段的类。当然,这根本没用——这又是从 还是 来的? 让我们重写查询,这次使用嵌套结果:id1latlonglat1long1lat1fromto

routesWithNestedPoints: SELECT r.id, r.name, f.** AS "from", t.** AS "to" FROM saved_routes rINNER JOIN coordinates f ON f.id = r."from"INNER JOIN coordinates t ON t.id = r."to";

如您所见,我们只需使用特定于漂移的 table.**语法即可嵌套结果。对于此查询,漂移将生成以下类:

class RoutesWithNestedPointsResult {final int id;final String name;final Point from;final Point to;// ...
}

太棒了!这个类比之前的平面结果类更符合我们的意图。

这些嵌套结果列 ( **) 只能出现在顶级 select 语句中,复合 select 语句或子查询尚不支持它们。但是,它们可以引用 SQL 中已连接到 select 语句的任何结果集,包括子查询和表值函数。

你可能想知道它的内部工作原理,因为它不是有效的 SQL。在构建时,drift 的生成器会将其转换为引用表的所有列的列表。例如,如果我们有一个foo包含id INT 和bar TEXT列的表。那么,SELECT foo.** FROM foo可能会被解析为 SELECT foo.id AS “nested_0.id”, foo.bar AS “nested_0”.bar FROM foo。

LIST子查询

从 Drift 版本开始1.4.0,子查询也可以作为完整列表进行选择。只需将子查询放在LIST()函数中,即可将子查询的所有行包含在结果集中。

重新使用嵌套结果示例中介绍的coordinates和表,我们添加一个存储沿路线坐标的新表:saved_routes

CREATE TABLE route_points (route INTEGER NOT NULL REFERENCES saved_routes (id),point INTEGER NOT NULL REFERENCES coordinates (id),index_on_route INTEGER,PRIMARY KEY (route, point)
);

现在,假设我们想要查询一条包含沿途所有点信息的路线。虽然这需要两条 SQL 语句,但我们可以将其写成一条漂移查询,然后自动拆分成两条语句:

routeWithPoints: SELECTroute.**,LIST(SELECT coordinates.* FROM route_pointsINNER JOIN coordinates ON id = pointWHERE route = route.idORDER BY index_on_route) AS pointsFROM saved_routes route;

这将生成一个结果集,其中包含一个SavedRoute route字段以及 List points沿途所有点的列表。

在内部,Drift 会将此查询拆分为两个单独的查询:- 外部SELECT route.** FROM saved_routes routeSQL 查询 -SELECT coordinates.* FROM route_points … ORDER BY index_on_route为外部查询中的每一行运行一个单独的查询。route.id内部查询中的引用将被替换为一个变量,Drift 会将该变量绑定到外部查询中的实际值。

虽然LIST()子查询是一个非常强大的功能,但当外部查询有很多行时(因为内部查询针对每个外部行执行),它们的成本可能很高。

Dart 互操作

Drift 文件与 Drift 现有的 dart_api 完美协同工作:

  • 您可以为漂移文件中声明的表编写 Dart 查询:
Future<void> insert(TodosCompanion companion) async {await into(todos).insert(companion);
}
  • 通过将 Dart 文件导入到漂移文件中,您可以为 Dart 中声明的表编写 SQL 查询。
  • 生成的查询方法可用于事务,它们与自动更新查询等一起工作。

如果您在生成的 Dart 类中使用fromJson和toJson方法,并且需要更改 json 中列的名称,则可以使用JSON KEY列约束来执行此操作,因此id INT NOT NULL JSON KEY userId 会在 json 中生成序列化为“userId”的列。

SQL 中的 Dart 组件

你可以使用“Dart 模板”来充分利用 SQL 和 Dart 语言的优势。Dart 模板是一种 Dart 表达式,可以在运行时内联到查询语句中。要使用它们,请在查询语句中声明一个 $ 变量:

filterTodos: SELECT * FROM todos WHERE $predicate;
Drift 将生成一个Selectable带有predicate参数的方法,可用于在运行时构建动态过滤器:

Stream<List<Todo>> watchInCategory(int category) {return filterTodos((todos) => todos.category.equals(category)).watch();
}

这让你可以编写单个 SQL 查询并在运行时动态应用谓词!此功能适用于

  • 表达式,正如您在上面的示例中看到的那样
  • 单一排序项:SELECT * FROM todos ORDER BY $term, id ASC
    将生成一个采用的方法OrderingTerm。
  • 整个 order-by 子句:SELECT * FROM todos ORDER BY $order
  • 限制条款:SELECT * FROM todos LIMIT $limit
  • 插入语句的插入项:INSERT INTO todos $row生成一个Insertable row 参数

当用作表达式时,您还可以在查询中提供默认值:

getTodos ($predicate = TRUE): SELECT * FROM todos WHERE $predicate;

这将使该参数在 Dart 中成为可选参数。如果未明确设置,predicate它将使用默认的 SQL 值(此处为)。TRUE

类型转换器

您可以在漂移文件中导入并使用用 Dart 编写的类型转换器 。导入 Dart 文件需要使用常规import语句。要在列定义上应用类型转换器,可以使用MAPPED BY列约束:

CREATE TABLE users (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,name TEXT,preferences TEXT MAPPED BY `const PreferenceConverter()`
);

引用带有类型转换器的表列的查询或视图也将继承该转换器。此外,查询和视图都可以为特定列指定类型转换器:

CREATE VIEW my_view AS SELECT 'foo' MAPPED BY `const PreferenceConverter()`SELECTid,json_extract(preferences, '$.settings') MAPPED BY `const PreferenceConverter()`
FROM users;

使用类型转换器时,我们推荐使用apply_converters_on_variables build 选项。这也会将转换器从 Dart 应用于 SQL,例如,如果用于变量:SELECT * FROM users WHERE preferences = ?。使用该选项,变量将被推断为 ,Preferences而不是String。

Drift 文件还对隐式枚举转换器有特殊支持:

import 'status.dart';CREATE TABLE tasks (id INTEGER NOT NULL PRIMARY KEY,status ENUM(Status)
);

当然,关于自动枚举转换器的警告也适用于漂移文件。

现有的行类

您可以使用自定义行类,而不必让 Drift 为您生成一个。例如,假设您有一个 Dart 类定义为

class User {final int id;final String name;User(this.id, this.name);
}

然后,您可以指示漂移将该类用作行类,如下所示:

import 'row_class.dart'; --import for where the row class is definedCREATE TABLE users (id INTEGER NOT NULL PRIMARY KEY,name TEXT NOT NULL
) WITH User; -- This tells drift to use the existing Dart class

当使用在其他 Dart 文件中定义的自定义行类时,您还需要将该文件导入到定义数据库的文件中。有关此功能的更多常规信息,请查看此页面。

自定义行类可应用于文件SELECT中定义的查询.drift。要使用自定义行类,WITH可在查询名称后添加语法。

例如,假设我们row_class.dart通过添加另一个类来扩展现有的 Dart 代码:

class UserWithFriends {final User user;final List<User> friends;UserWithFriends(this.user, {this.friends = const []});
}

现在,我们可以使用新类为其行添加相应的查询:

-- table to demonstrate a more complex select query below.
-- also, remember to add the import for `UserWithFriends` to your drift file.
CREATE TABLE friends (user_a INTEGER NOT NULL REFERENCES users(id),user_b INTEGER NOT NULL REFERENCES users(id),PRIMARY KEY (user_a, user_b)
);allFriendsOf WITH UserWithFriends: SELECT users.** AS user, LIST(SELECT * FROM users a INNER JOIN friends ON user_a = a.id WHERE user_b = users.id OR user_a = users.id
) AS friends FROM users WHERE id = :id;

该WITH UserWithFriends语法将使 Drift 考虑UserWithFriends类。对于构造函数中的每个字段,Drift 都会检查查询中的列,并验证其是否具有兼容类型。然后,Drift 会在内部生成查询代码,将行映射到该类的实例 UserWithFriends。

有关使用自定义行类进行查询的更完整概述,请参阅 查询部分。

Dart 文档注释

漂移文件中列前添加的注释将作为 Dart 文档注释添加到生成的行类中:

CREATE TABLE friends (-- The user original sending the friendship requestuser_a INTEGER NOT NULL REFERENCES users(id),-- The user accepting the friendship request from [userA].user_b INTEGER NOT NULL REFERENCES users(id),PRIMARY KEY (user_a, user_b)
);

漂移生成的类中生成的userA和字段将会有这些注释作为文档注释。userBFriend

结果类名称

对于大多数查询,漂移会生成一个新的类来保存结果。该类以查询名称加上后缀命名Result,例如,一个myQuery查询会得到一个MyQueryResult类。

您可以像这样更改结果类的名称:

routesWithNestedPoints AS FullRoute: SELECT r.id, -- ...

这样,多个查询也可以共享一个结果类。只要它们具有相同的结果集,您就可以为它们分配相同的自定义名称,并且漂移只会生成一个类。

对于仅选择表中所有列的查询,drift 不会生成新的类,而是会重用它生成的数据类。同样,对于只有一列的查询,drift 会直接返回该列,而不是将其包装在结果类中。目前无法覆盖此行为,因此,如果查询包含匹配的表或只有一列,则您无法自定义该查询的结果类名称。

支持的语句

目前,.drift文件中可以出现以下语句。

  • import ‘other.drift’:将另一个文件中声明的所有表和查询导入到当前文件中。
  • DDL 语句:您可以将CREATE TABLE、、CREATE VIEW和CREATE INDEX语句CREATE
    TRIGGER放入漂移文件中。
  • 查询语句:我们支持INSERT、、SELECT和UPDATE语句DELETE。

所有导入都必须位于 DDL 语句之前,并且这些语句必须位于命名查询之前。

如果您需要另一个语句的支持,或者如果漂移拒绝您认为有效的查询,请创建一个问题!

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

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

相关文章

【排坑指南】MySQL初始化后,Nacos与微服务无法连接??

Date&#xff1a;2025/06/18 你好&#xff01; 今天&#xff0c;分享一个工作中遇到的一个 MySQL 问题。在这之前都不知道是 MySQL 的问题&#xff0c;特离谱&#xff01; 昨天和今天大多数时间都用来处理了这一个问题&#xff1a;《MySQL进行了数据库初始化之后&#xff0c…

springboot获取工程目录

在springboot中使用ApplicationHome获取工程所在目录的时候&#xff0c;开发环境和生产运行环境输出的目录是不同的&#xff0c;开发环境到target/classes目录&#xff0c;而生产运行则是需要的wzkj-server.jar所在目录 ApplicationHome home new ApplicationHome(CollectTas…

深入ZGC并发处理的原理

大型Java应用的核心痛点之一&#xff1a;当JVM进行垃圾回收时强制程序暂停&#xff08;STW&#xff09;的代价。在要求低延迟的应用场景——高频交易系统、实时在线服务或全球性大型平台——中&#xff0c;这种"时空静止"的成本可能极高。但JDK从16版本&#xff08;生…

配置DHCP服务(小白的“升级打怪”成长之路)

目录 项目前准备 一、DHCP服务器配置&#xff08;Rocky8&#xff09; 1&#xff0c;关闭防火墙、安全上下文 2、配置网卡文件 3、安装hdcp-server 4、配置dhcp服务 5、重启dhcp服务 二、配置路由器 1、添加两块网卡并更改网卡配置文件 2、配置路由功能 3、挂载本地镜…

云原生安全

云原生 | T Wiki 以下大部分内容参考了这篇文章 什么是云原生 云原生&#xff08;Cloud Native&#xff09; “云原生”可以从字面上拆解为“云”和“原生”两个部分来理解&#xff1a; “云”&#xff0c;是相对于“本地”而言的。传统应用部署在本地数据中心或物理服务器…

rapidocr v3.2.0发布

粗略更新日志 rapidocr v3.2.0 发布了。令我感到很开心的是&#xff1a;有 3 个小伙伴提了 PR&#xff0c;他们积极参与了进来。 更新要点如下&#xff1a; 采纳了小伙伴qianliyx 的建议&#xff0c;按照行返回单字坐标&#xff1a;同一行的单字坐标是在同一个 tuple 中的。…

Java 操作数类型冲突: varbinary 与 real 不兼容, Java中BigDecimal与SQL Server real类型冲突解决方案

要解决Java中BigDecimal类型与SQL Server中real类型冲突导致的varbinary与real不兼容错误&#xff0c;请按以下步骤操作&#xff1a; 错误原因分析 类型映射错误&#xff1a;JDBC驱动尝试将BigDecimal转换为varbinary&#xff08;二进制类型&#xff09;&#xff0c;而非目标字…

25.多子句查询

MySQL 中包含 GROUP BY、HAVING、ORDER BY、LIMIT 时的查询语法规则及应用&#xff0c;核心知识总结如下&#xff1a; 1.语法顺序规则 当 SELECT 语句同时包含 GROUP BY、HAVING、ORDER BY、LIMIT 时&#xff0c;执行顺序为&#xff1a; GROUP BY → HAVING → ORDER BY → L…

Vue3 × DataV:三步上手炫酷数据可视化组件库

DataV&#xff08;kjgl77/datav-vue3&#xff09;是专为“大屏可视化”场景打造的 Vue3 组件库&#xff0c;提供边框、装饰、等数十个开箱即用的视觉组件。本文聚焦 “在 Vue3 项目中如何正确使用 DataV”&#xff0c;从安装、全局注册到常见坑点&#xff0c;带你迅速玩转这款酷…

本地KMS服务器激活常用命令

OpenWRT内置了KMS激活的相关服务&#xff0c;配置后需要电脑本地切换到该KMS服务。相关命令如下&#xff1a; 基本功能与定义‌ slmgr是Windows内置的软件授权管理工具&#xff0c;全称为Software License Manager。其核心功能包括产品密钥安装/卸载、许可证信息查询、KMS服务器…

存货核算:个别计价法、先进先出法、加权平均法、移动加权平均法解读

存货作为企业资产的重要组成部分&#xff0c;贯穿于企业运营的各个环节&#xff0c;特别是制造业&#xff0c;企业的所有运营体系都是围绕存货来开展的。根据会计准则&#xff0c;存货是指企业在日常活动中持有以备出售的产成品或商品、处在生产过程中的在半成品&#xff0c;以…

Java异步编程:挑战、实践与未来

&#x1f4cc; 摘要 在现代高并发、高性能的系统中&#xff0c;异步编程已经成为构建响应式应用的重要手段。Java 提供了多种异步编程模型&#xff0c;从最基础的 Future 和线程池&#xff0c;到 CompletableFuture 的链式调用&#xff0c;再到反应式框架如 Project Reactor 和…

哈希函数结构:从MD到海绵的进化之路

一、MD结构&#xff1a;哈希函数的经典范式 1. Merkle-Damgrd结构核心原理 #mermaid-svg-BX4ZrTendXiyIVr0 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-BX4ZrTendXiyIVr0 .error-icon{fill:#552222;}#mermaid-s…

零基础设计模式——行为型模式 - 模板方法模式

第四部分&#xff1a;行为型模式 - 模板方法模式 (Template Method Pattern) 现在我们来学习模板方法模式。这个模式在一个方法中定义一个算法的骨架&#xff0c;而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 核…

android通过adb push apk放置目录/sdcard/Download/下无法安装

本文通过对源码进行追踪,并且调试各种方式,得出android通过adb push apk放置目录/sdcard/Download/下无法安装的原因,并从两个修改点触发,提出如何能修复此问题的建议。 1. 现象 把apk通过adb push的方式放在/sdcard/Download文件夹下, (1)直接打开File(DocumentUI)…

Spring Boot整合PF4J:构建动态插拔的组件化架构

前言 在当今快速迭代的软件开发领域,业务需求的频繁变更对系统架构的灵活性和可扩展性提出了极高要求。传统的单体应用架构在面对功能的不断新增和修改时,往往会陷入代码臃肿、维护困难、扩展性差的困境。组件化开发,为解决这些问题提供了新的思路,通过实现组件的动态插拔…

剃须效率低?电铸多孔刀网设计如何提升毛发捕捉率

剃须效率低下常源于刀网对毛发的捕捉能力不足——传统冲压刀网因孔型单一、边缘毛刺等问题&#xff0c;导致胡须滑脱或拉扯。而电铸多孔刀网通过精密工艺革新&#xff0c;将毛发捕捉率提升40%以上。其核心优势在于三维立体孔型设计与微米级精度控制&#xff0c;以下是技术解析&…

进一步了解git

1、什么是集中式&#xff1f;什么是分布式&#xff1f; SVN&#xff08;集中式&#xff09; 单一中央仓库&#xff1a;所有代码和历史版本集中存储在中央服务器&#xff0c;用户本地仅保存当前工作副本。 强依赖网络&#xff1a;提交、查看历史等操作需实时连接服务器&#xf…

一、react18+项目初始化

npx create-rect-app 项目名称配置antd design mobile // 安装 npm install --save antd-mobile // 在文件中直接引入使用 import { Button } from antd-mobile <Button></Button>更改webpack配置 // 1.安装必要的包 npm install craco --save-dev // 2.修改pack…

Azure 资源清单

Azure 资源清单 作用前置条件安装PowerShell 7.0验证 Azure资源清单安装配置如果有旧版本&#xff0c;导致新模块安装不上&#xff0c;进行强制安装 PowerShell 登录到 Azure基本命令输出详细信息效果图展示 作用 官方文档&#xff1a;https://github.com/microsoft/ARI?tabr…