Go语言 GORM框架 使用指南
在 Go 语言社区中,数据库交互一直是开发者们关注的重点领域,不同开发者基于自身的需求和偏好,形成了两种主要的技术选型流派。一部分开发者钟情于像sqlx
这类简洁的库,尽管其功能并非一应俱全,但它赋予开发者对 SQL 语句的绝对控制权,便于开发者将性能优化到极致,从而满足对性能有严苛要求的场景。与之相对的另一部分开发者,则更倾向于为提升开发效率而生的 ORM(对象关系映射)框架。借助 ORM,开发者能够省去诸多繁琐的数据库操作细节,极大地加速开发进程。
在 Go 语言的 ORM 领域,gorm
无疑占据着举足轻重的地位,作为一款历史悠久且成熟的 ORM 框架,深受广大开发者的喜爱。与gorm
类似的,还有相对年轻的xorm
和ent
等框架,它们各自凭借独特的优势,在 Go 语言社区中也拥有着一批忠实的用户。
本文聚焦于gorm
框架,主要为大家介绍其基础入门知识,希望能为读者开启探索gorm
世界的大门。若想深入了解gorm
的更多细节,推荐阅读官方文档,其完善的中文文档为开发者提供了详尽的学习资料。
- 官方文档:GORM - The fantastic ORM library for Golang, aims to be developer friendly.
- 开源仓库:go-gorm/gorm: The fantastic ORM library for Golang, aims to be developer friendly (github.com)
特点
- 全功能 ORM:涵盖了丰富的数据库操作功能,为开发者提供一站式解决方案。
- 关联关系支持:全面支持多种关联关系,如拥有一个(Has One)、拥有多个(Has Many)、属于(Belongs To)、多对多(Many To Many)、多态(Polymorphism)以及单表继承(Single-table inheritance),满足复杂业务场景下的数据关系建模需求。
- 钩子方法:在 Create、Save、Update、Delete、Find 等操作中均提供了钩子方法,方便开发者在数据库操作前后进行自定义逻辑处理。
- 预加载功能:支持
Preload
和Joins
的预加载方式,有效减少数据库查询次数,提升数据获取效率。 - 事务管理:提供完善的事务管理机制,包括事务、嵌套事务、Save Point 以及 Rollback To Saved Point 等功能,确保数据操作的原子性和一致性。
- 多种模式支持:支持 Context、预编译模式(Prepared Statement Mode)和 DryRun 模式,为开发者提供更多的灵活性和调试便利性。
- 高效的数据操作:具备批量插入、FindInBatches、Find/Create with Map 等功能,并且支持使用 SQL 表达式、Context Valuer 进行 CRUD 操作,满足不同场景下的数据操作需求。
- 强大的 SQL 构建能力:拥有 SQL 构建器,支持 Upsert、锁机制、Optimizer/Index/Comment Hint、命名参数以及子查询等高级 SQL 特性,让开发者能够灵活构建复杂的 SQL 语句。
- 数据库结构管理:支持复合主键、索引和约束的创建与管理,同时提供自动迁移功能,能够根据定义的结构体自动同步数据库表结构,减少手动维护数据库结构的工作量。
- 自定义日志:允许开发者自定义 Logger,方便记录和跟踪数据库操作日志,便于排查问题和进行性能分析。
- 灵活的插件扩展:提供灵活可扩展的插件 API,例如 Database Resolver(支持多数据库、读写分离)、Prometheus 等插件,满足不同业务场景下的扩展需求。
- 严格的测试保障:每个特性都经过了严格的测试,确保框架的稳定性和可靠性。
- 开发者友好:设计理念注重开发者体验,提供简洁易懂的 API 和丰富的文档,降低开发者的学习成本。
当然,gorm
并非完美无缺。例如,其几乎所有方法的参数都采用空接口类型,这使得参数的传递方式较为模糊,若不查阅文档,开发者很难明确在不同场景下应传递何种参数,有时可传递结构体,有时是字符串、map 或切片。此外,在许多情况下,开发者仍需自行编写 SQL 语句来满足复杂的业务需求。
其中,gorm
作为 Go 生态中历史悠久的 ORM 框架,凭借其全面的功能支持和良好的社区活跃度,成为众多项目的首选。本文将围绕gorm
展开基础入门介绍,旨在帮助快速上手。
安装
$ go get -u gorm.io/gorm
gorm 目前支持以下几种数据库
- MySQL :
"gorm.io/driver/mysql"
- PostgreSQL:
"gorm.io/driver/postgres"
- SQLite:
"gorm.io/driver/sqlite"
- SQL Server:
"gorm.io/driver/sqlserver"
- TIDB:
"gorm.io/driver/mysql"
,TIDB 兼容 mysql 协议 - ClickHouse:
"gorm.io/driver/clickhouse"
本文接下来将使用 MySQL 来进行演示,使用的什么数据库,就需要安装什么驱动,这里安装 Mysql 的 gorm 驱动。
$ go get -u gorm.io/driver/mysql # 以MySQL为例
然后使用 dsn(data source name)连接到数据库,驱动库会自行将 dsn 解析为对应的配置
package mainimport ("gorm.io/driver/mysql""gorm.io/gorm""log/slog"
)func main() {dsn := "root:123456@tcp(192.168.48.138:3306)/hello?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn))if err != nil {slog.Error("db connect error", err)}slog.Info("db connect success")
}
或者手动传入配置
package mainimport ("gorm.io/driver/mysql""gorm.io/gorm""log/slog"
)func main() {db, err := gorm.Open(mysql.New(mysql.Config{}))if err != nil {slog.Error("db connect error", err)}slog.Info("db connect success")
}
两种方法都是等价的,看自己使用习惯。
连接配置
通过传入gorm.Config
配置结构体,我们可以控制 gorm 的一些行为
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
模型
gorm.Model
为了方便模型定义,GORM内置了一个gorm.Model
结构体。gorm.Model
是一个包含了ID
, CreatedAt
, UpdatedAt
, DeletedAt
四个字段的Golang结构体。
// gorm.Model 定义
type Model struct {ID uint `gorm:"primary_key"`CreatedAt time.TimeUpdatedAt time.TimeDeletedAt *time.Time
}
你可以将它嵌入到你自己的模型中:
// 将 `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`字段注入到`User`模型中
type User struct {gorm.Model // 内嵌gorm.Model,包含ID, CreatedAt, UpdatedAt, DeletedAt字段 Name string `gorm:"type:varchar(100);not null"`Email string `gorm:"type:varchar(100);uniqueIndex"`Age int `gorm:"default:18"`IsActive bool `gorm:"default:true"`
}
当然你也可以完全自己定义模型:
// 不使用gorm.Model,自行定义模型
type User struct {ID intName string
}
结构体标记(tags)
使用结构体声明模型时,标记(tags)是可选项。gorm支持以下标记:
支持的结构体标记(Struct tags)
结构体标记(Tag) | 描述 |
---|---|
Column | 指定列名 |
Type | 指定列数据类型 |
Size | 指定列大小, 默认值255 |
PRIMARY_KEY | 将列指定为主键 |
UNIQUE | 将列指定为唯一 |
DEFAULT | 指定列默认值 |
PRECISION | 指定列精度 |
NOT NULL | 将列指定为非 NULL |
AUTO_INCREMENT | 指定列是否为自增类型 |
INDEX | 创建具有或不带名称的索引, 如果多个索引同名则创建复合索引 |
UNIQUE_INDEX | 和 INDEX 类似,只不过创建的是唯一索引 |
EMBEDDED | 将结构设置为嵌入 |
EMBEDDED_PREFIX | 设置嵌入结构的前缀 |
- | 忽略此字段 |
关联相关标记(tags)
结构体标记(Tag) | 描述 |
---|---|
MANY2MANY | 指定连接表 |
FOREIGNKEY | 设置外键 |
ASSOCIATION_FOREIGNKEY | 设置关联外键 |
POLYMORPHIC | 指定多态类型 |
POLYMORPHIC_VALUE | 指定多态值 |
JOINTABLE_FOREIGNKEY | 指定连接表的外键 |
ASSOCIATION_JOINTABLE_FOREIGNKEY | 指定连接表的关联外键 |
SAVE_ASSOCIATIONS | 是否自动完成 save 的相关操作 |
ASSOCIATION_AUTOUPDATE | 是否自动完成 update 的相关操作 |
ASSOCIATION_AUTOCREATE | 是否自动完成 create 的相关操作 |
ASSOCIATION_SAVE_REFERENCE | 是否自动完成引用的 save 的相关操作 |
PRELOAD | 是否自动完成预加载的相关操作 |
主键、表名、列名的约定
主键(Primary Key)
GORM 默认会使用名为ID的字段作为表的主键。
type User struct {ID string // 名为`ID`的字段会默认作为表的主键Name string
}// 使用`AnimalID`作为主键
type Animal struct {AnimalID int64 `gorm:"primary_key"`Name stringAge int64
}
表名(Table Name)
表名默认就是结构体名称的复数,例如:
type User struct {} // 默认表名是 `users`// 将 User 的表名设置为 `profiles`
func (User) TableName() string {return "profiles"
}func (u User) TableName() string {if u.Role == "admin" {return "admin_users"} else {return "users"}
}// 禁用默认表名的复数形式,如果置为 true,则 `User` 的默认表名是 `user`
db.SingularTable(true)
也可以通过Table()
指定表名:
// 使用User结构体创建名为`deleted_users`的表
db.Table("deleted_users").CreateTable(&User{})var deleted_users []User
db.Table("deleted_users").Find(&deleted_users)
SELECT * FROM deleted_users;db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
DELETE FROM deleted_users WHERE name = 'jinzhu';
GORM还支持更改默认表名称规则:
gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string {return "prefix_" + defaultTableName;
}
列名(Column Name)
列名由字段名称进行下划线分割来生成
type User struct {ID uint // column name is `id`Name string // column name is `name`Birthday time.Time // column name is `birthday`CreatedAt time.Time // column name is `created_at`
}
可以使用结构体tag指定列名:
type Animal struct {AnimalId int64 `gorm:"column:beast_id"` // set column name to `beast_id`Birthday time.Time `gorm:"column:day_of_the_beast"` // set column name to `day_of_the_beast`Age int64 `gorm:"column:age_of_the_beast"` // set column name to `age_of_the_beast`
}
时间戳跟踪
CreatedAt
如果模型有 CreatedAt
字段,该字段的值将会是初次创建记录的时间。
db.Create(&user) // `CreatedAt`将会是当前时间// 可以使用`Update`方法来改变`CreateAt`的值
db.Model(&user).Update("CreatedAt", time.Now())
UpdatedAt
如果模型有UpdatedAt
字段,该字段的值将会是每次更新记录的时间。
db.Save(&user) // `UpdatedAt`将会是当前时间db.Model(&user).Update("name", "jinzhu") // `UpdatedAt`将会是当前时间
DeletedAt
如果模型有DeletedAt
字段,调用Delete
删除该记录时,将会设置DeletedAt
字段为当前时间,而不是直接将记录从数据库中删除。
CRUD接口
简单的列举了,创建、查询、修改、删除的使用,详情可以看官方文档。
创建 (Create)
// 创建单个记录
user := User{Name: "张三", Age: 20}
result := db.Create(&user)
// 执行SQL: INSERT INTO users (name, age) VALUES ('张三', 20);// 批量创建
users := []User{{Name: "李四", Age: 22},{Name: "王五", Age: 23},
}
db.Create(&users)
// 执行SQL: INSERT INTO users (name, age) VALUES ('李四', 22), ('王五', 23);
查询 (Read)
单条查询
var user User
db.First(&user)
// 执行SQL: SELECT * FROM users ORDER BY id LIMIT 1;db.Where("name = ?", "张三").First(&user)
// 执行SQL: SELECT * FROM users WHERE name = '张三' ORDER BY id LIMIT 1;db.First(&user, 10)
// 执行SQL: SELECT * FROM users WHERE id = 10;
多条查询
var users []User
db.Where("age > ?", 20).Find(&users)
// 执行SQL: SELECT * FROM users WHERE age > 20;db.Where(map[string]interface{}{"name": "张三", "age": 20}).Find(&users)
// 执行SQL: SELECT * FROM users WHERE name = '张三' AND age = 20;
高级查询
db.Select("name", "age").Find(&users)
// 执行SQL: SELECT name, age FROM users;db.Order("age desc").Find(&users)
// 执行SQL: SELECT * FROM users ORDER BY age DESC;db.Limit(10).Offset(5).Find(&users)
// 执行SQL: SELECT * FROM users LIMIT 10 OFFSET 5;var count int64
db.Model(&User{}).Where("age > ?", 20).Count(&count)
// 执行SQL: SELECT COUNT(*) FROM users WHERE age > 20;
更新 (Update)
db.Save(&user)
// 执行SQL: UPDATE users SET name='张三', age=20 WHERE id=1;db.Model(&user).Update("name", "李四")
// 执行SQL: UPDATE users SET name='李四' WHERE id=1;db.Model(&user).Updates(User{Name: "李四", Age: 21})
// 执行SQL: UPDATE users SET name='李四', age=21 WHERE id=1;db.Model(&User{}).Where("age < ?", 20).Update("name", "未成年人")
// 执行SQL: UPDATE users SET name='未成年人' WHERE age < 20;
删除 (Delete)
db.Delete(&user)
// 执行SQL: DELETE FROM users WHERE id=1;db.Delete(&User{}, 10)
// 执行SQL: DELETE FROM users WHERE id=10;db.Where("age < ?", 20).Delete(&User{})
// 执行SQL: DELETE FROM users WHERE age < 20;
事务
自动事务
db.Transaction(func(tx *gorm.DB) error {if err := tx.Create(&user1).Error; err != nil {return err}// 执行SQL: INSERT INTO users (name, age) VALUES ('user1', 20);if err := tx.Create(&user2).Error; err != nil {return err}// 执行SQL: INSERT INTO users (name, age) VALUES ('user2', 22);return nil
})
// 如果成功执行SQL: COMMIT;
// 如果失败执行SQL: ROLLBACK;
手动事务
tx := db.Begin()
// 执行SQL: BEGIN;tx.Create(&user1)
// 执行SQL: INSERT INTO users (name, age) VALUES ('user1', 20);tx.Create(&user2)
// 执行SQL: INSERT INTO users (name, age) VALUES ('user2', 22);tx.Commit()
// 执行SQL: COMMIT;// 或者出错时
tx.Rollback()
// 执行SQL: ROLLBACK;
嵌套事务
db.Transaction(func(tx *gorm.DB) error {tx.Create(&user1)// 执行SQL: INSERT INTO users (name, age) VALUES ('user1', 20);tx.Transaction(func(tx2 *gorm.DB) error {tx2.Create(&user2)// 执行SQL: SAVEPOINT sp1;// 执行SQL: INSERT INTO users (name, age) VALUES ('user2', 22);return errors.New("inner error")// 执行SQL: ROLLBACK TO sp1;})return nil// 执行SQL: COMMIT;
})
保存点 (SavePoint)
tx := db.Begin()
// 执行SQL: BEGIN;tx.Create(&user1)
// 执行SQL: INSERT INTO users (name, age) VALUES ('user1', 20);tx.SavePoint("sp1")
// 执行SQL: SAVEPOINT sp1;tx.Create(&user2)
// 执行SQL: INSERT INTO users (name, age) VALUES ('user2', 22);tx.RollbackTo("sp1")
// 执行SQL: ROLLBACK TO sp1;tx.Commit()
// 执行SQL: COMMIT;
参考资料:
GORM 指南
Golang 中文学习文档 - 第三方库 - GORM
李文周的博客 - GORM入门指南
李文周的博客 - GORM CRUD指南
相关文章:
Go语言 GORM框架 使用指南
在 Go 语言社区中,数据库交互一直是开发者们关注的重点领域,不同开发者基于自身的需求和偏好,形成了两种主要的技术选型流派。一部分开发者钟情于像sqlx这类简洁的库,尽管其功能并非一应俱全,但它赋予开发者对 SQL 语句…...
STM32控制电机
初始化时钟:在 STM32 的程序中,初始化系统时钟,一般会使用 RCC(Reset and Clock Control)相关函数来配置时钟。例如,对于 STM32F103 系列,可能会使用 RCC_APB2PeriphClockCmd 函数来使能 GPIO 和…...
力扣刷题(第二十九天)
灵感来源 - 保持更新,努力学习 - python脚本学习 验证回文串 解题思路 验证回文串的核心在于判断一个字符串是否从前向后和从后向前读都是一样的。不过,题目通常会有两个主要限制条件: 忽略大小写:比如 "A man" …...

chrome 浏览器插件 myTools, 日常小工具。
1. 起因, 目的: 比如,chatgpt, google, 打开网页,就能直接输入文字,然后 grok 就不行,必须用鼠标点一下,才能输入文字。 对我而言,是个痛点!写个插件,自动点…...
Leaflet使用SVG创建动态Legend
接前一篇文章,前一篇文章我们使用 SVG 创建了带有动态文字的图标,今天再看看怎样在地图上根据动态图标生成相关的legend,当然这里也还是使用了 SVG 来生成相关颜色的 legend。 看下面的代码,生成了一个 svg 节点,其中…...

智慧校园(含实验室)智能化专项汇报方案
该方案聚焦智慧校园(含实验室)智能化建设,针对传统实验室在运营监管、环境监测、安全管控、排课考勤等方面的问题,依据《智慧校园总体框架》等标准,设计数字孪生平台、实验室综合管理平台、消安电一体化平台三大核心平台,涵盖通信、安防、建筑设备管理等设施,涉及 395 个…...

第三十四节:特征检测与描述-SIFT/SURF 特征 (专利算法)
一、特征检测:计算机视觉的基石 在计算机视觉领域中,特征检测与描述是实现图像理解的核心技术。就像人类通过识别物体边缘、角点等特征来认知世界,算法通过检测图像中的关键特征点来实现: 图像匹配与拼接 物体识别与跟踪 三维重建 运动分析 其中,SIFT(Scale-Invariant F…...
ORACLE数据库实例报错ORA-00470: LGWR process terminated with error宕机问题分析报告
服务概述 10月21号03:22分,BOSS数据库实例发生异常宕机;工程师及时响应此问题并对此故障原因进行分析及相关建议,详细的故障情况及相关日志、TRACE文件的分析及总结、建议,请参阅本文档。 hzboss数据库实例宕机分析 4.1 数据库层面日志的分…...

【前端优化】vue2 webpack4项目升级webpack5,大大提升运行速度
记录一下过程 手里有个老项目,vue2webpack4 项目很大,每次运行、运行都要将近10分钟 现在又要往里面写很多东西,再不优化,开发着会更难受,所以决定先将它升级至webpack5 最初失败的尝试 直接在项目里安装了webpack5 但…...

Nginx应用场景详解与配置指南
1. 什么是Nginx? Nginx(发音为"engine-x")是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。它以高性能、稳定性、丰富的功能集、简单的配置和低资源消耗而闻名。 2. Nginx的主要应用场景 2.1 …...

vue2 切换主题色以及单页面好使方法
今天要新增一个页面要根据不同公司切换不同页面主题色,一点一点来,怎么快速更改 el-pagination 分页组件主题色。 <el-pagination :page-size"pageSize" :pager-count"pageCount"layout"sizes, prev, pager, next, jumper,…...
React学习———CSS Modules(样式模块化)
CSS Modules CSS Modules(样式模块化)是一种用于模块化和局部作用域化CSS样式的技术,让CSS只在当前组件内生效,避免全局样式冲突的技术方案 工作原理 文件命名:通常以.module.css、.module.less、.module.scss等结尾…...
MCP 与 Cloudflare 的结合:网络安全的新高度
MCP 与 Cloudflare 的结合:网络安全的新高度 在数字化时代,网络安全已经不只是某些行业的“专属问题”,而是所有企业、个人都必须面对的核心挑战。从 DDoS 攻击、数据泄露,到身份盗用,每一种网络威胁都可能带来巨大的损失。而这时候,微软 MCP(Microsoft Cloud Platform…...
JavaScript入门【1】概述
1.JavaScript是什么? <font style"color:rgb(38,38,38);">Javascript (简称“JS”)是⼀种直译式脚本语⾔,⼀段脚本其实就是⼀系列指令,计算机通过这些指令来达成⽬标。它⼜是⼀种动态类型的编程语⾔。JS⽤来在⽹…...
PyQt5 的使用
PyQt5 是 Python 里基于 Qt 框架的 GUI 开发工具,能做桌面应用,跨平台(Windows/macOS/Linux 都能用)。你可能想知道:怎么开始用?有哪些核心组件?怎么写界面逻辑?别急,咱们…...

JavaScript【6】事件
1.概述: 在 JavaScript 中,事件(Event)是浏览器或 DOM(文档对象模型)与 JavaScript 代码之间交互的一种机制。它代表了在浏览器环境中发生的特定行为或者动作,比如用户点击鼠标、敲击键盘、页面…...

STM32F10xx 参考手册
6. 什么是寄存器 本章参考资料:《STM32F10xx 参考手册》、《STM32F10xx数据手册》、 学习本章时,配合《STM32F10xx 参考手册》“存储器和总线架构”及“通用I/O(GPIO)”章节一起阅读,效果会更佳,特别是涉及到寄存器说明的部分。…...
使用Docker部署Nacos
sudo systemctl start docker sudo systemctl enable docker docker --version 步骤 2: 拉取 Nacos Docker 镜像 拉取 Nacos 镜像: 你可以从 Docker Hub 上拉取官方的 Nacos 镜像,使用以下命令: docker pull nacos/nacos-server 这会从 …...
深度学习中ONNX格式的模型文件
一、模型部署的核心步骤 模型部署的完整流程通常分为以下阶段,用 “跨国旅行” 类比: 步骤类比解释技术细节1. 训练模型学会一门语言(如中文)用 PyTorch/TensorFlow 训练模型2. 导出为 ONNX翻译成国际通用语言(如英语…...

TIFS2024 | CRFA | 基于关键区域特征攻击提升对抗样本迁移性
Improving Transferability of Adversarial Samples via Critical Region-Oriented Feature-Level Attack 摘要-Abstract引言-Introduction相关工作-Related Work提出的方法-Proposed Method问题分析-Problem Analysis扰动注意力感知加权-Perturbation Attention-Aware Weighti…...

Redis 发布订阅模式深度解析:原理、应用与实践
在现代分布式系统架构中,实时消息传递机制扮演着至关重要的角色。Redis 作为一款高性能的内存数据库,其内置的发布订阅(Pub/Sub)功能提供了一种轻量级、高效的消息通信方案。本文将全面剖析 Redis 发布订阅模式,从其基本概念、工作原理到实际…...
环形缓冲区 ring buffer 概述
环形缓冲区 ring buffer 概述 1. 简介 环形缓冲区(ring buffer),是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。也称作环形缓冲区(circular buffer),环形队列&…...

飞帆控件 post or get it when it has get
我在这里分享两个链接: post_get_it 设计 - 飞帆 有人看出来这个控件是干什么用吗? 控件的配置:...

SQL里where条件的顺序影响索引使用吗?
大家好,我是锋哥。今天分享关于【SQL里where条件的顺序影响索引使用吗?】面试题。希望对大家有帮助; SQL里where条件的顺序影响索引使用吗? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 SQL 查询中,W…...

SAP学习笔记 - 开发豆知识02 - com.sap.cds.services.cds.CdsService 废止,那么用什么代替呢?
我看很多人代码里面用的都是这个CdsService类,可以自从2.0版本往上这个类就没了啊。 它的代替是什么呢?用的CqnService 那么怎么查的呢? 我也是下载好几个包,解压看,然后发现这里还可以直接看,挺方便的。…...

OpenResty 深度解析:构建高性能 Web 服务的终极方案
引言 openresty是什么?在我个人对它的理解来看相当于嵌入了lua的nginx; 我们在nginx中嵌入lua是为了不需要再重新编译,我们只需要重新修改lua脚本,随后重启即可; 一.lua指令序列 我们分别从初始化阶段,重写/访问阶段,内容阶段,日志…...

什么是路由器环回接口?
路由器环回接口(LoopbackInterface)是网络设备中的一种逻辑虚拟接口,不依赖物理硬件,但在网络配置和管理中具有重要作用。以下是其核心要点: 一、基本特性 1.虚拟性与稳定性 环回接口是纯软件实现的逻辑接口&#x…...
OpenHarmony:开源操作系统重塑产业数字化底座
OpenHarmony:开源操作系统重塑产业数字化底座 引言:当操作系统成为数字公共品 在万物智联时代,操作系统不再是科技巨头的专属领地。华为捐赠的OpenHarmony项目,正以开源协作模式重构操作系统产业格局。这个脱胎于商业版本的开源…...

【MySQL进阶】如何在ubuntu下安装MySQL数据库
前言 🌟🌟本期讲解关于如何在ubuntu环境下安装mysql的详细介绍~~~ 🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 🔥 你的点赞就是小编不断更新的最大动力 dz…...

【数据结构】_二叉树
1.二叉树链式结构的实现 1.1 前置说明 在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树&#x…...