C# SQLite基本使用示例

目录

1 基本使用流程

1.1 步骤1:添加SQLite依赖

1.2 ​步骤2:建立连接

1.3 步骤3:执行SQL命令

1.4 步骤4:查询数据

1.5 步骤5:使用事务

2 SQLite基本使用示例

2.1 准备工作

2.2 完整示例

2.3 案例代码解析

2.3.1 连接管理

2.3.2 执行非查询命令

2.3.3 参数化查询

2.3.4 使用事务

2.3.5 查询和结果处理


本节将通过代码示例展示如何在C#中使用SQLite数据库。这个示例包含了创建连接、创建表、执行CRUD操作、事务处理和使用参数化查询等基本功能。在C#应用程序中使用SQLite是一个常见的需求,特别是对于需要轻量级数据存储的应用,将详细总结C#中使用SQLite数据库的基本流程,并解释案例中的代码含义和一些重要注意事项。

1 基本使用流程

1.1 步骤1:添加SQLite依赖

在C#项目中使用SQLite,首先需要添加适当的NuGet包:

System.Data.SQLite (完整版)
或
Microsoft.Data.Sqlite (轻量版,.NET Core/.NET 5+推荐)

通过Visual Studio的NuGet包管理器或命令行添加:

Install-Package System.Data.SQLite

dotnet add package Microsoft.Data.Sqlite

1.2 ​步骤2:建立连接

// 引入命名空间
using System.Data.SQLite;
​
// 连接字符串
string connectionString = "Data Source=database.db;Version=3;";
​
// 创建连接
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{connection.Open();// 执行数据库操作...// 使用using语句确保连接被正确关闭
}

1.3 步骤3:执行SQL命令

// 执行非查询命令(DDL或DML)
using (SQLiteCommand command = new SQLiteCommand(connection))
{command.CommandText = "CREATE TABLE IF NOT EXISTS Users (Id INTEGER PRIMARY KEY, Name TEXT, Email TEXT)";command.ExecuteNonQuery();
}
​
// 插入数据
using (SQLiteCommand command = new SQLiteCommand(connection))
{command.CommandText = "INSERT INTO Users (Name, Email) VALUES (@Name, @Email)";command.Parameters.AddWithValue("@Name", "张三");command.Parameters.AddWithValue("@Email", "zhangsan@example.com");command.ExecuteNonQuery();
}

1.4 步骤4:查询数据

// 查询数据
using (SQLiteCommand command = new SQLiteCommand("SELECT * FROM Users", connection))
{using (SQLiteDataReader reader = command.ExecuteReader()){while (reader.Read()){int id = reader.GetInt32(0);string name = reader.GetString(1);string email = reader.GetString(2);Console.WriteLine($"ID: {id}, Name: {name}, Email: {email}");}}
}

1.5 步骤5:使用事务

using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{connection.Open();using (SQLiteTransaction transaction = connection.BeginTransaction()){try{// 执行多个数据库操作using (SQLiteCommand command = new SQLiteCommand(connection, transaction)){command.CommandText = "INSERT INTO Users (Name) VALUES (@Name)";command.Parameters.AddWithValue("@Name", "李四");command.ExecuteNonQuery();command.CommandText = "UPDATE Users SET Email = @Email WHERE Name = @Name";command.Parameters.AddWithValue("@Email", "lisi@example.com");command.Parameters.AddWithValue("@Name", "李四");command.ExecuteNonQuery();}// 如果一切正常,提交事务transaction.Commit();}catch (Exception ex){// 发生错误,回滚事务transaction.Rollback();Console.WriteLine($"事务回滚: {ex.Message}");}}
}

2 SQLite基本使用示例

2.1 准备工作

首先,你需要安装SQLite的C#驱动。在Visual Studio中,通过NuGet包管理器安装System.Data.SQLiteMicrosoft.Data.Sqlite

// 使用NuGet包管理器控制台安装
// Install-Package System.Data.SQLite
// 或者
// Install-Package Microsoft.Data.Sqlite

2.2 完整示例

using System;
using System.Data.SQLite; // 使用System.Data.SQLite包
using System.IO;namespace SQLiteDemo
{class Program{// 数据库连接字符串private static string dbFile = "mydatabase.db";private static string connectionString = $"Data Source={dbFile};Version=3;";static void Main(string[] args){try{// 确保数据库文件存在,如果不存在则创建CreateDatabaseIfNotExists();// 创建表CreateTables();// 插入数据InsertData();// 查询数据QueryData();// 更新数据UpdateData();// 使用事务批量插入BatchInsertWithTransaction();// 删除数据DeleteData();// 参数化查询示例ParameterizedQueryExample();Console.WriteLine("所有操作已完成!");}catch (Exception ex){Console.WriteLine($"发生错误: {ex.Message}");Console.WriteLine(ex.StackTrace);}Console.ReadKey();}// 创建数据库文件static void CreateDatabaseIfNotExists(){if (!File.Exists(dbFile)){Console.WriteLine("创建新的数据库文件...");SQLiteConnection.CreateFile(dbFile);Console.WriteLine("数据库文件已创建。");}else{Console.WriteLine("使用现有数据库文件。");}}// 创建表static void CreateTables(){using (var connection = new SQLiteConnection(connectionString)){connection.Open();Console.WriteLine("已连接到数据库。");// 创建用户表string createUserTableSql = @"CREATE TABLE IF NOT EXISTS Users (Id INTEGER PRIMARY KEY AUTOINCREMENT,Name TEXT NOT NULL,Email TEXT UNIQUE,Age INTEGER,RegisterDate TEXT DEFAULT CURRENT_TIMESTAMP)";// 创建订单表,演示外键关系string createOrderTableSql = @"CREATE TABLE IF NOT EXISTS Orders (OrderId INTEGER PRIMARY KEY AUTOINCREMENT,UserId INTEGER,ProductName TEXT NOT NULL,Quantity INTEGER DEFAULT 1,OrderDate TEXT DEFAULT CURRENT_TIMESTAMP,FOREIGN KEY (UserId) REFERENCES Users(Id))";using (var command = new SQLiteCommand(createUserTableSql, connection)){command.ExecuteNonQuery();Console.WriteLine("Users表已创建。");}using (var command = new SQLiteCommand(createOrderTableSql, connection)){command.ExecuteNonQuery();Console.WriteLine("Orders表已创建。");}}}// 插入数据static void InsertData(){using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 插入用户数据string insertUserSql = @"INSERT INTO Users (Name, Email, Age) VALUES (@Name, @Email, @Age)";using (var command = new SQLiteCommand(insertUserSql, connection)){// 添加参数command.Parameters.AddWithValue("@Name", "张三");command.Parameters.AddWithValue("@Email", "zhangsan@example.com");command.Parameters.AddWithValue("@Age", 28);int rowsAffected = command.ExecuteNonQuery();Console.WriteLine($"插入了 {rowsAffected} 行用户数据。");// 获取自增IDstring getLastIdSql = "SELECT last_insert_rowid()";using (var idCommand = new SQLiteCommand(getLastIdSql, connection)){long userId = (long)idCommand.ExecuteScalar();Console.WriteLine($"新插入用户的ID是: {userId}");// 为该用户添加订单string insertOrderSql = @"INSERT INTO Orders (UserId, ProductName, Quantity)VALUES (@UserId, @ProductName, @Quantity)";using (var orderCommand = new SQLiteCommand(insertOrderSql, connection)){orderCommand.Parameters.AddWithValue("@UserId", userId);orderCommand.Parameters.AddWithValue("@ProductName", "笔记本电脑");orderCommand.Parameters.AddWithValue("@Quantity", 1);rowsAffected = orderCommand.ExecuteNonQuery();Console.WriteLine($"插入了 {rowsAffected} 行订单数据。");}}}}}// 查询数据static void QueryData(){using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 简单查询string queryUsersSql = "SELECT * FROM Users";using (var command = new SQLiteCommand(queryUsersSql, connection)){Console.WriteLine("\n所有用户:");using (var reader = command.ExecuteReader()){while (reader.Read()){Console.WriteLine($"ID: {reader["Id"]}, 姓名: {reader["Name"]}, " +$"邮箱: {reader["Email"]}, 年龄: {reader["Age"]}, " +$"注册日期: {reader["RegisterDate"]}");}}}// 联表查询string queryOrdersSql = @"SELECT o.OrderId, u.Name as UserName, o.ProductName, o.Quantity, o.OrderDateFROM Orders oJOIN Users u ON o.UserId = u.IdORDER BY o.OrderDate DESC";using (var command = new SQLiteCommand(queryOrdersSql, connection)){Console.WriteLine("\n所有订单:");using (var reader = command.ExecuteReader()){while (reader.Read()){Console.WriteLine($"订单ID: {reader["OrderId"]}, 用户: {reader["UserName"]}, " +$"商品: {reader["ProductName"]}, 数量: {reader["Quantity"]}, " +$"订单日期: {reader["OrderDate"]}");}}}// 聚合查询string aggregateQuerySql = @"SELECT COUNT(*) as UserCount, AVG(Age) as AvgAgeFROM Users";using (var command = new SQLiteCommand(aggregateQuerySql, connection)){using (var reader = command.ExecuteReader()){if (reader.Read()){Console.WriteLine($"\n统计信息: 用户总数: {reader["UserCount"]}, 平均年龄: {reader["AvgAge"]}");}}}}}// 更新数据static void UpdateData(){using (var connection = new SQLiteConnection(connectionString)){connection.Open();string updateUserSql = @"UPDATE Users SET Age = @NewAge, Name = @NewNameWHERE Email = @Email";using (var command = new SQLiteCommand(updateUserSql, connection)){command.Parameters.AddWithValue("@NewAge", 29);command.Parameters.AddWithValue("@NewName", "张三(已更新)");command.Parameters.AddWithValue("@Email", "zhangsan@example.com");int rowsAffected = command.ExecuteNonQuery();Console.WriteLine($"\n更新了 {rowsAffected} 行用户数据。");}}}// 使用事务批量插入static void BatchInsertWithTransaction(){using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 开始事务using (var transaction = connection.BeginTransaction()){try{string insertUserSql = @"INSERT INTO Users (Name, Email, Age) VALUES (@Name, @Email, @Age)";// 批量插入10个用户using (var command = new SQLiteCommand(insertUserSql, connection, transaction)){for (int i = 1; i <= 10; i++){command.Parameters.Clear();command.Parameters.AddWithValue("@Name", $"批量用户{i}");command.Parameters.AddWithValue("@Email", $"user{i}@example.com");command.Parameters.AddWithValue("@Age", 20 + i);command.ExecuteNonQuery();}}// 提交事务transaction.Commit();Console.WriteLine("\n事务批量插入成功,已添加10位用户。");}catch (Exception ex){// 出错时回滚事务transaction.Rollback();Console.WriteLine($"\n事务回滚: {ex.Message}");}}}}// 删除数据static void DeleteData(){using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 删除指定用户string deleteUserSql = "DELETE FROM Users WHERE Name LIKE @NamePattern";using (var command = new SQLiteCommand(deleteUserSql, connection)){command.Parameters.AddWithValue("@NamePattern", "批量用户%");int rowsAffected = command.ExecuteNonQuery();Console.WriteLine($"\n删除了 {rowsAffected} 行用户数据。");}}}// 参数化查询示例 - 防止SQL注入static void ParameterizedQueryExample(){using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 这是安全的方式,使用参数化查询string safeQuerySql = "SELECT * FROM Users WHERE Name LIKE @NamePattern";using (var command = new SQLiteCommand(safeQuerySql, connection)){// 即使用户输入包含SQL注入尝试,也会被当作普通字符串处理string userInput = "张三' OR '1'='1";command.Parameters.AddWithValue("@NamePattern", "%" + userInput + "%");Console.WriteLine("\n执行参数化查询(防止SQL注入):");using (var reader = command.ExecuteReader()){bool hasResults = false;while (reader.Read()){hasResults = true;Console.WriteLine($"ID: {reader["Id"]}, 姓名: {reader["Name"]}");}if (!hasResults){Console.WriteLine("没有找到匹配的记录。");}}}// 注意: 这里演示如何使用执行单值查询string countSql = "SELECT COUNT(*) FROM Users";using (var command = new SQLiteCommand(countSql, connection)){int count = Convert.ToInt32(command.ExecuteScalar());Console.WriteLine($"\n数据库中共有 {count} 个用户。");}}}}
}

2.3 案例代码解析

2.3.1 连接管理

static void SetupExampleData()
{using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 数据库操作...}
}

解析:

  • using语句确保即使发生异常,连接也会被正确释放和关闭,避免资源泄漏

  • connection.Open()建立与SQLite数据库文件的物理连接

  • 连接字符串格式通常为Data Source=文件路径;Version=3;,其中文件路径可以是相对或绝对路径

2.3.2 执行非查询命令

static void ExecuteNonQuery(SQLiteConnection connection, string sql, SQLiteTransaction transaction = null)
{using (var command = new SQLiteCommand(sql, connection, transaction)){command.ExecuteNonQuery();}
}

解析:

  • 这是一个辅助方法,用于执行不返回结果集的SQL命令(如CREATE, INSERT, UPDATE, DELETE)

  • 参数化设计允许重用并支持事务

  • ExecuteNonQuery()返回受影响的行数(对于DDL语句如CREATE TABLE通常返回0)

  • 同样使用using语句确保命令资源被正确释放

2.3.3 参数化查询

ExecuteNonQuery(connection, @"INSERT INTO Employees (Name, DeptId, Salary, HireDate) VALUES('张三', 1, 15000, '2020-01-15')");

更安全的参数化版本:

using (var command = new SQLiteCommand(connection))
{command.CommandText = "INSERT INTO Employees (Name, DeptId, Salary, HireDate) VALUES (@Name, @DeptId, @Salary, @HireDate)";command.Parameters.AddWithValue("@Name", "张三");command.Parameters.AddWithValue("@DeptId", 1);command.Parameters.AddWithValue("@Salary", 15000);command.Parameters.AddWithValue("@HireDate", "2020-01-15");command.ExecuteNonQuery();
}

解析:

  • 参数化查询防止SQL注入攻击

  • 使用@前缀的参数名称是约定

  • AddWithValue方法会自动根据传入参数的类型设置SQLite参数类型

2.3.4 使用事务

using (var transaction = connection.BeginTransaction())
{try{// 执行多个操作ExecuteNonQuery(connection, "INSERT INTO Departments (DeptId, Name) VALUES (4, '人力资源部')", transaction);ExecuteNonQuery(connection, "UPDATE Employees SET Salary = Salary * 1.1 WHERE DeptId = 1", transaction);// 假设这里有条件判断是否提交bool shouldCommit = true;if (shouldCommit){transaction.Commit();Console.WriteLine("事务已提交。");}else{transaction.Rollback();Console.WriteLine("事务已回滚。");}}catch (Exception ex){transaction.Rollback();Console.WriteLine($"发生错误,事务已回滚: {ex.Message}");}
}

解析:

  • BeginTransaction()开始一个新事务

  • 事务内的所有操作要么全部成功,要么全部失败

  • Commit()提交所有更改并结束事务

  • Rollback()撤销所有更改并结束事务

  • try-catch块确保在发生异常时事务被正确回滚

2.3.5 查询和结果处理

static void DisplayQueryResults(SQLiteConnection connection, string sql)
{using (var command = new SQLiteCommand(sql, connection)){using (var reader = command.ExecuteReader()){// 获取列名List<string> columns = new List<string>();for (int i = 0; i < reader.FieldCount; i++){columns.Add(reader.GetName(i));}// 输出列名Console.WriteLine(string.Join(" | ", columns));Console.WriteLine(new string('-', columns.Count * 15));// 输出行while (reader.Read()){List<string> values = new List<string>();for (int i = 0; i < reader.FieldCount; i++){values.Add(reader[i]?.ToString() ?? "NULL");}Console.WriteLine(string.Join(" | ", values));}}}
}

解析:

  • ExecuteReader()返回一个SQLiteDataReader对象,用于逐行读取结果集

  • reader.FieldCount获取结果集的列数

  • reader.GetName(i)获取特定列的名称

  • reader.Read()将读取指针移到下一行,如果有下一行则返回true

  • reader[i]获取当前行中索引为i的列的值

  • ?? "NULL"处理可能的NULL值

  • 嵌套的using语句确保读取器和命令都被正确关闭

3 数据类型映射

C#类型和SQLite类型之间的映射:

C#类型SQLite类型
int, longINTEGER
double, floatREAL
stringTEXT
byte[]BLOB
DateTimeTEXT/INTEGER
boolINTEGER

在读取时需要注意类型转换,SQLite的动态类型系统可能需要额外的类型转换处理。

4 使用注意事项

  1. 连接管理

    •  总是使用using语句确保连接正确关闭

    • 避免长时间保持连接打开

    • 对于多线程应用,考虑使用连接池 

  2. 参数化查询

    • 始终使用参数化查询防止SQL注入

    • 不要通过字符串拼接构建SQL语句

  3. 事务使用

    • 批量操作使用事务提高性能

    • 保持事务尽可能短小

    • 正确处理事务的异常情况

  4. 性能考虑

    • 为频繁查询的列创建索引

    • 使用EXPLAIN QUERY PLAN分析查询性能

    • 大量数据操作时考虑批处理

  5. 版本兼容性

    • 确认目标SQLite版本是否支持需要的功能

    • 窗口函数需要SQLite 3.25+

    • FTS5需要特殊编译支持

以上就是关于如何在C#中使用SQLite数据库的基本流程,下一节我们将通过其他的C#示例代码来讲解SQLite高级功能的实现!!!

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

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

相关文章

视频图像压缩领域中 DCT 的 DC 系数和 AC 系数详解

引言 在数字图像与视频压缩领域&#xff0c;离散余弦变换&#xff08;Discrete Cosine Transform, DCT&#xff09;凭借其卓越的能量集中特性&#xff0c;成为JPEG、MPEG等国际标准的核心技术。DCT通过将空域信号映射到频域&#xff0c;分离出DC系数&#xff08;直流分量&…

对抗系统熵增:从被动救火到主动防御的稳定性实战

&#x1f4d5;我是廖志伟&#xff0c;一名Java开发工程师、《Java项目实战——深入理解大型互联网企业通用技术》&#xff08;基础篇&#xff09;、&#xff08;进阶篇&#xff09;、&#xff08;架构篇&#xff09;清华大学出版社签约作家、Java领域优质创作者、CSDN博客专家、…

java 中 DTO 和 VO 的核心区别

DTO 和 VO 的核心区别 特性DTO&#xff08;数据传输对象&#xff09;VO&#xff08;视图对象&#xff09;设计目的服务层与外部系统&#xff08;如前端、其他服务&#xff09;之间的数据传输为前端展示层定制数据&#xff0c;通常与 UI 强绑定数据内容可能包含业务逻辑需要的字…

数据结构【二叉树的遍历实现】

&#x1f4d8;考研数据结构基础&#xff1a;二叉树的存储、遍历与队列辅助实现详 在数据结构的学习中&#xff0c;二叉树作为一种结构清晰、应用广泛的树形结构&#xff0c;是考研计算机专业课中重点内容之一。本文将以实际代码为基础&#xff0c;介绍二叉树的存储结构、遍历方…

无人机俯视风光摄影Lr调色预设,手机滤镜PS+Lightroom预设下载!

调色详情 无人机俯视风光摄影 Lr 调色是利用 Adobe Lightroom 软件&#xff0c;对无人机从俯视角度拍摄的风光照片进行后期处理的调色方式。通过调整色彩、对比度、光影等多种参数&#xff0c;能够充分挖掘并强化画面独特视角下的壮美与细节之美&#xff0c;让原本平凡的航拍风…

【springcloud学习(dalston.sr1)】Eureka服务端集群的搭建(含源代码)(二)

该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍&#xff08;含源代码&#xff09;&#xff08;一&#xff09; 这篇文章主要介绍多个eureka服务端的集群环境是如何搭建的。 &#xff08;一&#xff09;eureka的简要说明 Eu…

互联网大厂Java求职面试实战:Spring Boot微服务与数据库优化详解

&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&#xff0c;毕业季咱们不慌忙&#xff0c;几百款毕业设计等你选。 ❤️ 3. Python爬虫专栏…

事件驱动reactor的原理与实现

fdset 集合&#xff1a;&#xff08;就是说&#xff09; fd_set是一个位图&#xff08;bitmap&#xff09;结构 每个位代表一个文件描述符 0表示不在集合中&#xff0c;1表示在集合中 fd_set结构&#xff08;简化&#xff09;&#xff1a; [0][1][2][3][4][5]...[1023] …

一分钟在Cherry Studio和VSCode集成火山引擎veimagex-mcp

MCP的出现打通了AI模型和外部数据库、网页API等资源&#xff0c;成倍提升工作效率。近期火山引擎团队推出了 MCP Server SDK&#xff1a; veimagex-mcp。本文介绍如何在Cherry Studio 和VSCode平台集成 veimagex-mcp。 什么是MCP MCP&#xff08;Model Context Protocol&…

掌控随心 - 服务网格的流量管理艺术 (Istio 实例)

掌控随心 - 服务网格的流量管理艺术 (Istio 实例) 想象一下,没有服务网格的时候,我们要实现像“将 1% 的用户流量导入到新版本应用”、“根据用户设备类型访问不同后端”、“模拟下游服务故障”这类高级流量策略,通常需要在代码、负载均衡器、API 网关等多个地方进行复杂且分…

[ARM][汇编] 01.基础概念

目录 1.全局标号 1.1.使用方法 1.1.1.声明全局标号 1.1.2.定义全局标号 1.1.3.引用全局标号 1.2.全局标号与局部标号的区别 1.3.注意事项 2.局部标号 2.1.使用方法 2.1.1.定义局部标号 2.1.2.跳转引用 2.2.局部标号与全局标号的对比 2.3.注意事项 3.符号定义伪指…

如何使用远程桌面控制电脑

目的&#xff1a; 通过路由器使用pc控制台式机&#xff0c;实现了有线/无线pc与台式机的双向远程桌面控制 最核心就两条&#xff1a;get ip地址与被控制机器的账户与密码。 现象挺神奇&#xff1a;被控制电脑的电脑桌面处于休眠模式&#xff0c;此时强行唤醒被控电脑会导致中断…

Hive表JOIN性能问

在处理100TB的Hive表JOIN性能问题时&#xff0c;需采用分层优化策略&#xff0c;结合数据分布特征、存储格式和计算引擎特性。以下是系统性优化方案&#xff1a; 1. 数据倾斜优化&#xff08;Skew Join&#xff09; 1.1 识别倾斜键 方法&#xff1a;统计JOIN键的分布频率&…

MongoDB 的核心概念(文档、集合、数据库、BSON)是什么?

MongoDB 是一个面向文档的数据库&#xff0c;它的核心概念与传统的关系型数据库&#xff08;RDBMS&#xff09;有所不同。以下是它的四个主要核心概念&#xff1a; 文档 (Document) 定义&#xff1a; 文档是 MongoDB 中的基本数据单元。它类似于关系型数据库中的一行记录&#…

AI智慧公园管理方案:用科技重塑市民的“夜游体验”

AI智慧公园管理方案&#xff1a;多场景智能巡检与安全防控 一、背景与痛点分析 夏季夜间&#xff0c;公园成为市民休闲娱乐的核心场所&#xff0c;但管理难度随之激增&#xff1a; 宠物管理失控&#xff1a;未牵绳宠物进入园区&#xff0c;随地排泄、惊扰游客&#xff0c;甚…

Spring Cloud Gateway 聚合 Swagger 文档:一站式API管理解决方案

前言 在微服务架构中&#xff0c;随着服务数量的增加&#xff0c;API文档管理变得越来越复杂。每个微服务都有自己的Swagger文档&#xff0c;开发人员需要记住每个服务的文档地址&#xff0c;这无疑增加了开发难度。本文将介绍如何使用Spring Cloud Gateway聚合所有微服务的Sw…

尼康VR镜头防抖模式NORMAL和ACTIVE的区别(私人笔记)

1. NORMAL 模式&#xff08;常规模式&#xff09; 适用场景&#xff1a;一般手持拍摄&#xff0c;比如人像、静物、风景或缓慢平移镜头&#xff08;如水平追拍&#xff09;等。工作特性&#xff1a; 补偿手抖引起的小幅度震动&#xff08;比如手持时自然的不稳&#xff09;&am…

Babylon.js学习之路《四、Babylon.js 中的相机(Camera)与视角控制》

文章目录 1. 引言&#xff1a;为什么相机是 3D 场景的“眼睛”&#xff1f;1.1 相机的核心作用1.2 常见相机类型概览 2. 相机基础参数解析2.1 通用属性2.2 相机坐标系 3. 详解常用相机类型3.1 自由相机&#xff08;FreeCamera&#xff09;3.2 弧形旋转相机&#xff08;ArcRotat…

【Python】普通方法、类方法和静态方法的区分

Python 中普通方法、类方法和静态方法的区分 下面我将从多个维度对这三种方法进行详细对比&#xff0c;并通过示例说明它们的使用场景和区别。 1. 核心区别总结 特性普通方法(实例方法)类方法(classmethod)静态方法(staticmethod)定义装饰器无classmethodstaticmethod第一个…

geoserver发布arcgis瓦片地图服务(最新版本)

第一步&#xff1a;下载geoserver服务&#xff0c;进入bin目录启动 需要提前安装好JDK环境&#xff0c;1.8及以上版本 安装完成&#xff0c;页面访问端口&#xff0c;进入控制台界面,默认用户名密码admin/geoserver 第二步&#xff1a;下载地图 破解版全能电子地图下载器&…