【GORM学习笔记】GORM介绍以及增删改查相关操作
优缺点
- 优点:提高开发效率,防止SQL注入、对不熟悉SQL语句的人友好、代码统一
- 缺点:牺牲执行能力、牺牲灵活性、弱化SQL能力
在一些小型项目上使用ORM可以大大提高开发效率,但是在一些对性能要求高得场景下,ORM可能没有那么合适。
环境配置
-
GORM:在命令行中输入如下命令
go get -u github.com/jinzhu/gorm
即可安装GORM。 -
MySQL:请参考MySQL安装教程。
连接数据库
-
GROM官方支持的数据库类型有:MySQL、PostgreSQL, SQLite, SQL Server 和 TiDB。
-
以连接MySQL为例,GORM连接MySQL非常简单,其代码如下:
import ("gorm.io/driver/mysql""gorm.io/gorm" )func main() {dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) }
-
MySQL驱动程序也提供了一些高级配置供程序员在初始化的过程中使用。
db, err := gorm.Open(mysql.New(mysql.Config{DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source nameDefaultStringSize: 256, // string 类型字段的默认长度DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置 }), &gorm.Config{})
模型默认相关
-
GORM提供了一个预定义的结构体,名为
gorm.Model
,其中包含常用字段:ID为默认的主键;CreatedAt记录了该记录被创建的时间;UpdatedAt记录该记录被更新的时间;DeletedAt记录了该记录被删除的时间,因为GORM中的Delete是软删除。// gorm.Model 的定义 type Model struct {ID uint `gorm:"primaryKey"`CreatedAt time.TimeUpdatedAt time.TimeDeletedAt gorm.DeletedAt `gorm:"index"` }
-
将其嵌入结构体中: 可以直接在结构体中嵌入
gorm.Model
,以便自动包含这些字段。 这对于在不同模型之间保持一致性并利用GORM内置的约定非常有用。当然,我们也可以手动创建。 -
包含的字段:
ID
:每个记录的唯一标识符(主键)。CreatedAt
:在创建记录时自动设置为当前时间。UpdatedAt
:每当记录更新时,自动更新为当前时间。DeletedAt
:用于软删除(将记录标记为已删除,而实际上并未从数据库中删除)
-
GORM中常见的结构体tag:
标签名 说明 column 指定 db 列名 type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如: not null
、size
,autoIncrement
… 像varbinary(8)
这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
serializer 指定将数据序列化或反序列化到数据库中的序列化器, 例如: serializer:json/gob/unixtime
size 定义列数据类型的大小或长度,例如 size: 256
primaryKey 将列定义为主键 unique 将列定义为唯一键 default 定义列的默认值 precision 指定列的精度 scale 指定列大小 not null 指定列为 NOT NULL autoIncrement 指定列为自动增长 autoIncrementIncrement 自动步长,控制连续记录之间的间隔 embedded 嵌套字段 embeddedPrefix 嵌入字段的列名前缀 autoCreateTime 创建时追踪当前时间,对于 int
字段,它会追踪时间戳秒数,您可以使用nano
/milli
来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano
autoUpdateTime 创建/更新时追踪当前时间,对于 int
字段,它会追踪时间戳秒数,您可以使用nano
/milli
来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
index 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情 uniqueIndex 与 index
相同,但创建的是唯一索引check 创建检查约束,例如 check:age > 13
,查看 约束 获取详情<- 设置字段写入的权限, <-:create
只创建、<-:update
只更新、<-:false
无写入权限、<-
创建和更新权限-> 设置字段读的权限, ->:false
无读权限- 忽略该字段, -
表示无读写,-:migration
表示无迁移权限,-:all
表示无读写迁移权限comment 迁移时为字段添加注释 -
使用方法如下:
type User struct {gorm.ModelUserName string `gorm:"column:user_name;type:varchar(20);not null"`Age string `gorm:"column:age;type:int"`Email string `gorm:"column:email;type:varchar(20);unique"` }
-
创建记录
-
我们可以通过
gorm.Create()
方法创建记录,需要注意的是传递的参数是结构体指针,而非结构体本身。此外,gorm.Create()
方法也可以创建多条记录。返回值Result
有两个属性,分别为创建记录错误Error
和创建记录的条数RowAffected
。// 创建一条记录 user := User{Name: "xiaoming", Age: 21, Email: "XXX@qq.com"} result := db.Create(&user) // 通过数据的指针来创建// 创建多条记录 users := []*User{{Name: "xiaoming", Age: 21, Email: "XXX@qq.com"},{Name: "ming", Age: 21, Email: "YYY@qq.com"}, } result := db.Create(users) // 传递切片
-
创建记录并为指定字段赋值。
db.Select("Name", "Age", "CreatedAt").Create(&user)
-
创建记录并忽略传递给 ‘Omit’ 的字段值
db.Omit("Name", "Age", "CreatedAt").Create(&user)
-
使用钩子函数:GORM允许用户通过实现这些接口
BeforeSave
,BeforeCreate
,AfterSave
,AfterCreate
来自定义钩子。 这些钩子方法会在创建一条记录时被调用。func (u *User) BeforeCreate(tx *gorm.DB) (err error) {u.UUID = uuid.New()if u.Role == "admin" {return errors.New("invalid role")}return }
如果你想跳过
Hooks
方法,可以使用SkipHooks
会话模式,例子如下DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)
查询记录
-
GORM提供了
First
、Take
和Last
方法从数据库中检索单个对象。 -
First
andLast
方法会按主键排序找到第一条记录和最后一条记录 (分别)。 只有在目标struct是指针或者通过db.Model()
指定 model 时,该方法才有效。 此外,如果相关model没有定义主键,那么将按model的第一个字段进行排序。 例如:var user User var users []User// works because destination struct is passed in db.First(&user) // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1// works because model is specified using `db.Model()` result := map[string]interface{}{} db.Model(&User{}).First(&result) // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1// doesn't work result := map[string]interface{}{} db.Table("users").First(&result)// works with Take result := map[string]interface{}{} db.Table("users").Take(&result)// no primary key defined, results will be ordered by first field (i.e., `Code`) type Language struct {Code stringName string } db.First(&Language{}) // SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1
-
如果主键是数字类型,可以使用来检索对象。 当使用字符串时,需要额外的注意来避免SQL注入。
db.First(&user, 10) // SELECT * FROM users WHERE id = 10;db.First(&user, "10") // SELECT * FROM users WHERE id = 10;db.Find(&users, []int{1,2,3}) // SELECT * FROM users WHERE id IN (1,2,3);
如果主键是字符串(例如像uuid),查询将被写成如下:
db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a") // SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";
当目标对象有一个主键值时,将使用主键构建查询条件,例如:
var user = User{ID: 10} db.First(&user) // SELECT * FROM users WHERE id = 10;var result User db.Model(User{ID: 10}).First(&result) // SELECT * FROM users WHERE id = 10;
如果对象设置了主键,条件查询将不会覆盖主键的值,而是用And连接条件。 例如:
var user = User{ID: 10} db.Where("id = ?", 20).First(&user) // SELECT * FROM users WHERE id = 10 and id = 20 ORDER BY id ASC LIMIT 1
这个查询将会给出
record not found
错误 所以,在你想要使用例如user
这样的变量从数据库中获取新值前,需要将例如id
这样的主键设置为nil。 -
检索全部对象
result := db.Find(&users) // SELECT * FROM users;
-
我们可以使用
Limit
方法限制查询结果的条数result := db.Limit(1).Find(&users) // SELECT * FROM users LIMIT 1;
-
使用struct和map作为条件进行查询
// Struct db.Where(&User{Name: "jinzhu", Age: 20}).First(&user) // SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;// Map db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users) // SELECT * FROM users WHERE name = "jinzhu" AND age = 20;// Slice of primary keys db.Where([]int64{20, 21, 22}).Find(&users) // SELECT * FROM users WHERE id IN (20, 21, 22);
-
个人觉得这一部分和原生的MySQL查询非常地像,只是将一些条件变成了函数,另外一些条件写在
Where
方法中作为内联条件,当然,内联条件也可以写在First
和Find
中。
更新记录
-
Save
会更新所有的字段,即使字段是零值。db.First(&user)user.Name = "jinzhu 2" user.Age = 100 db.Save(&user) // UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
-
Update
可以更新单个列,Updates
可以更新多列,Updates
方法支持struct
和map[string]interface{}
参数。使用struct更新时,Updates
将只更新非零字段。// 更新单列 // User 的 ID 是 `111` db.Model(&user).Update("name", "hello") // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;// 更新多列 // 根据 `struct` 更新属性,只会更新非零值的字段 db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false}) // UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;// 根据 `map` 更新属性 db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false}) // UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
-
在更新时可以选择或者忽略某些字段,如果需要选择某些字段,可以使用
Select
;如果需要忽略某些字段,则可以使用Omit
。// 选择 Map 的字段 // User 的 ID 是 `111`: db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false}) // UPDATE users SET name='hello' WHERE id=111;db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false}) // UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;// 选择 Struct 的字段(会选中零值的字段) db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0}) // UPDATE users SET name='new_name', age=0 WHERE id=111;// 选择所有字段(选择包括零值字段的所有字段) db.Model(&user).Select("*").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})// 选择除 Role 外的所有字段(包括零值字段的所有字段) db.Model(&user).Select("*").Omit("Role").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})
-
GORM 支持的 hook 包括:
BeforeSave
,BeforeUpdate
,AfterSave
,AfterUpdate
. 更新记录时将调用这些方法。func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {if u.Role == "admin" {return errors.New("admin user not allowed to update")}return }
-
上述提到的方法均会调用钩子函数,如果想不调用钩子函数,则可以使用
UpdateColumn
和UpdateCoulmns
方法,使用方式类似Update
和Updates
。 -
阻止全局更新
如果你执行一个没有任何条件的批量更新,GORM 默认不会运行,并且会返回
ErrMissingWhereClause
错误你可以用一些条件,使用原生 SQL 或者启用
AllowGlobalUpdate
模式,例如:db.Model(&User{}).Update("name", "jinzhu").Error // gorm.ErrMissingWhereClausedb.Model(&User{}).Where("1 = 1").Update("name", "jinzhu") // UPDATE users SET `name` = "jinzhu" WHERE 1=1db.Exec("UPDATE users SET name = ?", "jinzhu") // UPDATE users SET name = "jinzhu"db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu") // UPDATE users SET `name` = "jinzhu"
删除记录
-
删除记录时需要指定主键,否则会触发批量删除
// User的ID是1 db.Delete(&user) // DELETE from users where id = 10;// User没有ID db.Delete(&user) // DELETE from users;
-
当然,我们可以使用带额外条件的删除避免批量删除
db.Where("name = ?", "xiaoming").Delete(&user) // DELETE from users where name = "xiaoming";
-
使用钩子函数
对于删除操作,GORM 支持
BeforeDelete
、AfterDelete
等钩子函数,在删除记录时会调用这些方法。func (u *User) BeforeDelete(tx *gorm.DB) (err error) { if u.Role == "admin" { return errors.New("admin user not allowed to delete") } return }
-
阻止全局删除
当你试图执行不带任何条件的批量删除时,GORM将不会运行并返回
ErrMissingWhereClause
错误如果一定要这么做,你必须添加一些条件,或者使用原生SQL,或者开启
AllowGlobalUpdate
模式,如下例:db.Delete(&User{}).Error // gorm.ErrMissingWhereClausedb.Delete(&[]User{{Name: "jinzhu1"}, {Name: "jinzhu2"}}).Error // gorm.ErrMissingWhereClausedb.Where("1 = 1").Delete(&User{}) // DELETE FROM `users` WHERE 1=1db.Exec("DELETE FROM users") // DELETE FROM usersdb.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{}) // DELETE FROM users
-
软删除
如果模型中包含了
gorm.DeleteAt
字段,则模型会自动获得软删除的能力,反之则没有软删除的能力。在有软删除能力的情况下调用
Delete
函数,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt
设置为当前时间,而后的一般查询方法将无法查找到此条记录。 -
查找被软删除的记录:可以使用
gorm.Unscoped()
方法实现。db.Unscoped().Where("age = 21").Find(&users)
-
永久删除:可以使用
Unscoped
来永久删除匹配的记录// User的ID是1 db.Unscoped().Delete(&user) // DELETE FROM order WHERE id=1;
-
删除标志
默认情况下,
gorm.Model
使用*time.Time
作为DeletedAt
的字段类型,不过软删除插件gorm.io/plugin/soft_delete
同时也提供其他的数据格式支持提示 当使用DeletedAt创建唯一复合索引时,你必须使用其他的数据类型,例如通过
gorm.io/plugin/soft_delete
插件将字段类型定义为unix时间戳等等import "gorm.io/plugin/soft_delete"type User struct {ID uintName string `gorm:"uniqueIndex:udx_name"`DeletedAt soft_delete.DeletedAt `gorm:"uniqueIndex:udx_name"` }
- Unix 时间戳:使用unix时间戳作为删除标志
import "gorm.io/plugin/soft_delete"type User struct {ID uintName stringDeletedAt soft_delete.DeletedAt }// 查询 SELECT * FROM users WHERE deleted_at = 0;// 软删除 UPDATE users SET deleted_at = /* current unix second */ WHERE ID = 1;
- 使用
1
/0
作为 删除标志
import "gorm.io/plugin/soft_delete"type User struct {ID uintName stringIsDel soft_delete.DeletedAt `gorm:"softDelete:flag"` }// 查询 SELECT * FROM users WHERE is_del = 0;// 软删除 UPDATE users SET is_del = 1 WHERE ID = 1;
- 混合模式:混合模式可以使用
0
,1
或者unix时间戳来标记数据是否被软删除,并同时可以保存被删除时间
type User struct {ID uintName stringDeletedAt time.TimeIsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"` // use `1` `0`// IsDel soft_delete.DeletedAt `gorm:"softDelete:,DeletedAtField:DeletedAt"` // use `unix second`// IsDel soft_delete.DeletedAt `gorm:"softDelete:nano,DeletedAtField:DeletedAt"` // use `unix nano second` }// 查询 SELECT * FROM users WHERE is_del = 0;// 软删除 UPDATE users SET is_del = 1, deleted_at = /* current unix second */ WHERE ID = 1;
参考文章
- [【官方文档】GORM指南](GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.)
相关文章:
【GORM学习笔记】GORM介绍以及增删改查相关操作
优缺点 优点:提高开发效率,防止SQL注入、对不熟悉SQL语句的人友好、代码统一缺点:牺牲执行能力、牺牲灵活性、弱化SQL能力 在一些小型项目上使用ORM可以大大提高开发效率,但是在一些对性能要求高得场景下,ORM可能没有…...
WebSocket在分布式环境中的局限性及解决方案
WebSocket 在分布式环境中存在一些局限性,特别是当系统需要扩展多个服务实例时,单个 WebSocket 连接的管理和消息推送就变得比较复杂。因此,必须采取一些额外的措施来确保 WebSocket 能在多个服务实例之间正确工作。 WebSocket 在分布式环境…...
SIM盾构建安全底座的可行性分析
一、背景 1.1安全需求现状 在数字化时代,信息安全面临着日益严峻的挑战。各类网络攻击手段层出不穷,如数据泄露、恶意软件攻击、网络诈骗等,给个人、企业和社会带来了巨大的损失。为了保障信息系统的安全性,需要构建一个可靠的安…...

【Java八股文】10-数据结构与算法面试篇
【Java八股文】10-数据结构与算法面试篇 数据结构与算法面试题数据结构红黑树说一下跳表说一下?LRU是什么?如何实现?布隆过滤器怎么设计?时间复杂度? 排序算法排序算法及空间复杂度 数据结构与算法面试题 数据结构 红…...

go 并发 gorouting chan channel select Mutex sync.One
goroutine // head: 前缀 index:是一个int的指针 func print(head string, index *int) {for i : 0; i < 5; i {// 指针对应的int *indexfmt.Println(*index, head, i)// 暂停1stime.Sleep(1 * time.Second)} }/* Go 允许使用 go 语句开启一个新的运…...

亲测Windows部署Ollama+WebUI可视化
一. Ollama下载 登录Ollama官网(Ollama)点击Download进行下载 如果下载很慢可用以下地址下载: https://github.com/ollama/ollama/releases/download/v0.5.7/OllamaSetup.exe 在DeepSeek官网上,你可以直接点击【model】 到达这个界面之后,…...

linux 安装启动zookeeper全过程及遇到的坑
1、下载安装zookeeper 参考文章:https://blog.csdn.net/weixin_48887095/article/details/132397448 2、启动失败 1、启动失败JAVA_HOME is not set and java could not be found in PATH 已安装 JAVA 配置了JAVA_HOME,还是报错解决方法:参考…...
策略模式Spring框架下开发实例
策略类Spring框架下开发实例 先列出策略模式下需要那些类: 策略接口 (Strategy),定义所有策略类必须遵循的行为。 具体策略类(如 ConcreteStrategyA、ConcreteStrategyB),实现不同的算法或行为。 上下文类 (Context),…...
DeepSeek模型量化
技术背景 大语言模型(Large Language Model,LLM),可以通过量化(Quantization)操作来节约内存/显存的使用,并且降低了通讯开销,进而达到加速模型推理的效果。常见的就是把Float16的浮…...
【练习】【回溯:组合:不同集合】力扣 17. 电话号码的字母组合
题目 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 示例 1: 输入:digits “2…...

分布式文件系统HDFS
一、HDFS简介 HDFS( Hadoop Distributed File System ),意为:Hadoop分布式文件系统。是Apache Hadoop核心组件之一,作为大数据生态圈最底层的分布式存储服务而存在。分布式文件系统解决大数据如何存储问题。分布式意味…...

从WebRTC到EasyRTC:嵌入式适配的视频通话SDK实现低延迟、高稳定性音视频通信
WebRTC最初是为浏览器之间的实时通信设计的,其资源需求和复杂性可能对嵌入式设备的性能提出较高要求,因此在嵌入式系统中应用时面临一些挑战: 1)资源消耗较高 CPU和内存占用:WebRTC是一个功能强大的实时通信框架&…...

WordPress自定义排序插件:Simple Custom Post Order完全指南(SEO优化版)
在WordPress建站中,文章、分类目录或页面的默认排序方式往往无法满足个性化需求。WordPress自定义排序插件:Simple Custom Post Order插件,你可以轻松实现拖拽式自定义排序,无需修改代码即可优化内容展示逻辑。本文将详细介绍这款…...
docker安装ros2 并在windows中显示docker内ubuntu系统窗口并且vscode编程
这里包括docker desktop安装ros2 humble hawkshill , 安装xserver(用来在windows中显示ubuntu中窗口), vscode安装插件连接docker并配置python的一系列方法 1.安装xserver 为了能方便的在windows中显示ubuntu内的窗口,比如rqt窗口 参考文章:https://www.cnblogs.com/larva-zhh…...
【QT中的一些高级数据结构,持续更新中...】
QT中有一些很精妙、便捷的设计,在了解这些数据的同时,我们可以学到如何更好的设计代码。本贴持续更新中,欢迎关注和收藏 一 QScopedPointer主要特点:示例代码 二 Q_DISABLE_COPY 一 QScopedPointer QScopedPointer 是 Qt 中的一种…...
简单工厂模式 (Simple Factory Pattern) 在Spring Boot 中的应用
简单工厂模式(Simple Factory Pattern)虽然不属于 GoF 23 种经典设计模式,但在实际开发中非常常用,尤其是在 Spring Boot 项目中。它提供了一种简单的方式来创建对象,将对象的创建逻辑集中到一个工厂类中。 一、简单工…...

《95015网络安全应急响应分析报告(2024)》
2025年2月,95015服务平台发布了最新一期的《95015网络安全应急响应分析报告(2024)》。报告分别从整体形势、受害者特征、攻击者特征等方面,对2024年95015平台接报的739起网络安全应急响应事件展开分析,并给出了7个年度…...
TensorFlow v2.16 Overview
TensorFlow v2.16 Overview 一、模块 Modules二、类 Classes三、函数 Functions TensorFlow v2.16.1 Overview 一、模块 Modules 模块是TensorFlow中组织代码的一种方式,将相关的功能和类封装在一起,方便用户使用和管理。每个模块都提供了特定领域的公共…...
Udp发送和接收数据(python和QT)
服务端代码 (python) import socketdef udp_server(host0.0.0.0, port12345):# 创建一个UDP套接字sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 绑定服务器的IP地址和端口号sock.bind((host, port))print(f"UDP服务器已启动,监听端口 {port}...&…...
element-plus 根据条件显示多选框
代码如下: <el-table :data"pager.lists" selection-change"handleSelectionChange" row-key"id" :tree-props"{ checkStrictly: true }" :cell-class-name"cellClass"> <el-table-column type"s…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...

使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...

软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...