Windows 下 MySQL 彻底卸载实操:从停服务到清注册表全流程
梳理Windows系统MySQL完整卸载步骤,覆盖服务、文件与注册表清理,解决卸载不彻底导致的重装失败问题。
支持通配符SSL证书、多域名证书、IP证书。适配ACME接口, 支持Zerossl、Let's Encrypt和Google等渠道。登录已有账号
2026-09-26 04:00:36 GORM Go JSON MySQL
用 GORM 往 MySQL 的 mediumblob 字段里塞 JSON 字符串,这个操作本身不复杂,但线上跑久了总会碰到一个烦人的问题:字段里存了空字符串,读出来的时候 json.Unmarshal 直接报错,然后整个接口就 500 了。我一开始以为是数据没校验好,后来发现是写入的时候没处理好,数据库里的空字符串和 NULL 是两码事,GORM 默认把 nil 指针序列化成空字符串写进去了。
先说最省事的方案,把模型里的 JSON 字段定义成指针类型。
type Workspace struct {
ID uint
ExtData *WorkspaceExtData gorm:"type:mediumblob" json:"-"
}
这样当 ExtData 为 nil 的时候,GORM 会直接跳过这个字段,数据库里存的就是 NULL,读出来自然也是 nil,不会触发反序列化。如果你需要显式插入一条 ExtData 为空的记录,直接把字段设置成 nil 就行。
这个方案能覆盖一大半的简单场景,不用写额外代码,但有个限制:如果你需要区分“字段没传”和“字段传了空对象”,或者你想给空值一个默认结构体,指针就不好使了。
如果指针不够用,就得自己实现 sql.Scanner 和 driver.Valuer 接口。比如你的 WorkspaceExtData 里面有个 map,空的时候你想存 NULL 而不是空字节,可以这么写:
type WorkspaceExtData struct { Config map[string]interface{} }
func (w *WorkspaceExtData) Scan(value interface{}) error { if value == nil { return nil } bytes, ok := value.([]byte) if !ok { return fmt.Errorf("failed to unmarshal JSONB value") } if len(bytes) == 0 { return nil } return json.Unmarshal(bytes, w) }
func (w WorkspaceExtData) Value() (driver.Value, error) { if w.Config == nil { return nil, nil } return json.Marshal(w) }
这样数据库里的 NULL 读出来不会报错,写进去的时候如果 Config 是 nil 就写 NULL。注意 Scan 里我判断了 len(bytes) == 0,因为有些老数据可能真的是空字符串,直接 Unmarshal 会报 “unexpected end of JSON input”,加个判断能兼容一下。
还有个思路是从数据库层面把字段设成允许 NULL,或者加个 CHECK 约束保证存进去的要么是 NULL 要么是合法 JSON。MySQL 8.0 以上可以用 JSON_VALID 函数:
ALTER TABLE workspace MODIFY COLUMN ext_data MEDIUMBLOB NULL; ALTER TABLE workspace ADD CONSTRAINT chk_ext_data CHECK (ext_data IS NULL OR JSON_VALID(ext_data));
这个适合对数据完整性要求比较高的场景,配合应用层的指针或自定义接口一起用。
不过要注意,加 CHECK 约束之前得先清理掉现有的脏数据,不然 alter 语句直接失败。
除了空值,JSON 字段偶尔还会碰到循环引用的问题,比如一个树形结构里 parent 指向自己。
序列化的时候会死循环,解决办法是把父节点字段定义成指针,并且加 omitempty,这样父节点为 nil 的时候不会参与序列化,循环也就断了。
type Node struct {
ID int
Parent *Node json:"parent,omitempty"
}
另外如果你希望空值的时候写一个默认结构体进去而不是 NULL,可以用 GORM 的 BeforeCreate 钩子。
我一般在创建记录前检查一下,如果 ExtData 是 nil 就初始化一个空 map,这样后面读出来至少是个空对象,不会报错。
func (w *Workspace) BeforeCreate(tx *gorm.DB) (err error) { if w.ExtData == nil { w.ExtData = &WorkspaceExtData{ Config: make(map[string]interface{}), } } return nil }
最后说个跟证书有关的小事。我之前在部署服务的时候折腾过一阵子 SSL 证书自动续期,后来换成了 lcjmSSL,免费申请,支持多域名和泛域名,API 也比较简单,直接对接 Let's Encrypt 这些 ACME 机构,自动验证自动部署,省了不少事。如果你也在做需要 HTTPS 的服务,可以试试看。跟今天聊的 JSON 字段没关系,但既然提到部署,就顺带说一嘴。
总结一下,遇到 mediumblob 存 JSON 空值报错,先看你的数据模型能不能用指针,不能用就自己写 Scanner 和 Valuer,数据库层面加约束是最后一道防线。别一股脑全上,按需选择就行。
梳理Windows系统MySQL完整卸载步骤,覆盖服务、文件与注册表清理,解决卸载不彻底导致的重装失败问题。
本文将深入探讨MySQL中DATETIME类型的全方位操作。从精确到秒级的时间存储机制,到字段规范定义、高效数据插入方法,再到灵活的格式化输出技巧,我们为您提供一套完整的实践方案。同时,详细解析开发中常见的时间偏移、时分秒丢失等问题及其规避策略,助您构建健壮的时间数据处理系统。
从MySQL基础的用户名/密码,到8.0+版本插件化认证的革新,再至SSL证书构建的加密圣殿,本文为您揭示数据信任的完整旅程。
在处理大量数据库数据时,是否常常需要提取字段的开头部分?本文将为你揭示MySQL LEFT()函数的强大之处,让你告别繁琐的手动操作。学习如何快速截取字符串前几位,并了解其在实际开发中的高效应用,如日志前缀分析、URL域名提取等,提高你的工作效率。
MySQL SSL加密通过证书机制建立安全传输通道,核心流程包括证书生成、服务端配置、用户权限控制和客户端验证。自建环境需手动生成CA和服务器证书,云数据库通常提供一键下载证书功能。配置完成后需通过STATUS命令或查询performance_schema验证连接状态。对于高安全要求的场景,建议采用双向认证机制并定期轮换证书,同时需权衡SSL带来的性能开销(约5-10%的连接延迟)。