惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
P
Palo Alto Networks Blog
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
NISL@THU
NISL@THU
L
Lohrmann on Cybersecurity
有赞技术团队
有赞技术团队
The GitHub Blog
The GitHub Blog
C
Cisco Blogs
B
Blog
Microsoft Azure Blog
Microsoft Azure Blog
Recent Announcements
Recent Announcements
Simon Willison's Weblog
Simon Willison's Weblog
T
Tenable Blog
Know Your Adversary
Know Your Adversary
Spread Privacy
Spread Privacy
WordPress大学
WordPress大学
月光博客
月光博客
Latest news
Latest news
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threat Research - Cisco Blogs
Cisco Talos Blog
Cisco Talos Blog
I
InfoQ
D
Darknet – Hacking Tools, Hacker News & Cyber Security
W
WeLiveSecurity
Hacker News - Newest:
Hacker News - Newest: "LLM"
酷 壳 – CoolShell
酷 壳 – CoolShell
U
Unit 42
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
Google DeepMind News
Google DeepMind News
Apple Machine Learning Research
Apple Machine Learning Research
Attack and Defense Labs
Attack and Defense Labs
罗磊的独立博客
T
The Exploit Database - CXSecurity.com
I
Intezer
GbyAI
GbyAI
Jina AI
Jina AI
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
Google Online Security Blog
Google Online Security Blog
Engineering at Meta
Engineering at Meta
D
Docker
Recent Commits to openclaw:main
Recent Commits to openclaw:main
小众软件
小众软件
云风的 BLOG
云风的 BLOG
爱范儿
爱范儿
Project Zero
Project Zero

希仁之拥

领克900半年使用体验 | 希仁之拥的博客 Ubuntu 26.04 Desktop使用体验 | 希仁之拥的博客 【转载】谈谈不受欢迎的博客技术特征 | 希仁之拥的博客 【转载】ClaudeCode 你想知道的所有秘密,源码深度研究报告 | 希仁之拥的博客 2025年年终总结 | 希仁之拥的博客 集成和使用Openclaw后的思考 | 希仁之拥的博客 我买了领克900 | 希仁之拥的博客 服务器性能优化之io拷贝 | 希仁之拥的博客 Go-Sail导航站上线啦 | 希仁之拥的博客 今年国庆的一些感受 [2025] | 希仁之拥的博客 在Deepin 25上配置forticlient | 希仁之拥的博客 分享一些酷酷的站点 [20250908] | 希仁之拥的博客 Go-Sail发布v3.0.6版本了 | 希仁之拥的博客 我对V2EX发布$V2EX讨论的一些感受 | 希仁之拥的博客 如何让Stripe支持支付宝和微信支付 | 希仁之拥的博客 2025上半年里程碑 | 希仁之拥的博客 GitLab+Drone使用体验 | 希仁之拥的博客 四姑娘山之旅 | 希仁之拥的博客 近来帮同事做性能优化的过程回顾 | 希仁之拥的博客 聊聊接口的返回数据结构 | 希仁之拥的博客 我把 Go-Sail 的文档站更新了 | 希仁之拥的博客 这就是我为什么讨厌拼多多 | 希仁之拥的博客 元旦快乐~ | 希仁之拥的博客 致敬还在写博客的我们 | 希仁之拥的博客 逐步的把图片资源迁移到星光图床上 | 希仁之拥的博客 帮弟弟配了一台mini主机 | 希仁之拥的博客 国庆的一些碎碎念 | 希仁之拥的博客 就这一刻而言,我觉得科技冷冰冰的。 | 希仁之拥的博客 如何使用acme.sh自动续签证书 | 希仁之拥的博客 Go-Sail的文档站点搭建好了 | 希仁之拥的博客 我的第一个谷歌浏览器插件发布成功啦! | 希仁之拥的博客 我开源了一个golang的web框架 | 希仁之拥的博客 流芳 | 希仁之拥的博客 第一次开发微信小程序 | 希仁之拥的博客 春节快乐 | 希仁之拥的博客 我把博客站点重写了 | 希仁之拥的博客 阆中古城 | 希仁之拥的博客 入了把Keydous NJ80 | 希仁之拥的博客 Mac上有用的知识 | 希仁之拥的博客 记一次服务器被攻击 | 希仁之拥的博客 Ubuntu server不停机扩容系统盘 | 希仁之拥的博客 端午安康 | 希仁之拥的博客 搭建hadoop伪分布式集群总结 | 希仁之拥的博客 在goa框架中,如何访问原始请求数据 | 希仁之拥的博客 postgresql相关操作笔记 | 希仁之拥的博客 golang解析带命名空间的xml数据(带冒号的节点或属性名) | 希仁之拥的博客 【转载】解决deepin环境下wine QQ无法接收图片问题 | 希仁之拥的博客 杂项笔记 | 希仁之拥的博客 Mac如何修改通知横幅消失时间 | 希仁之拥的博客 Golang校验结构体是否实现了接口 | 希仁之拥的博客 关于deepin os的大半年使用体验 | 希仁之拥的博客 有态度的翻唱 | 希仁之拥的博客 【转载】Golang 在 Mac、Linux、Windows 下如何交叉编译 | 希仁之拥的博客 和幺幺一起成长的半年时光 | 希仁之拥的博客 Element UI中message消息提示实现原理 | 希仁之拥的博客 从predis组件开始了解如何与redis服务交互 | 希仁之拥的博客 【转载】 尤雨溪谈Vue.js :缔造自由与真我 | 希仁之拥的博客 【转载】百万 Go TCP 连接的思考: epoll方式减少资源占用 | 希仁之拥的博客 【分享】移除git中的commit历史(保留文件,仅移除提交历史信息) | 希仁之拥的博客 shell学习笔记 | 希仁之拥的博客 【分享】Ruby 之父松本行弘:程序员的乐趣、存在感与平衡 | 希仁之拥的博客 安利一下我用过的一些sublime text 3工具、插件及配置 | 希仁之拥的博客 PHP处理苹果内购数据验证 | 希仁之拥的博客 PHP获取XML所有节点名称 | 希仁之拥的博客 Sentinel::check() 在Laravel5.3版本之后总是返回false的解决方案 | 希仁之拥的博客 吃货的美食集,持续更新~ | 希仁之拥的博客 第二十一届成都汽车博览会 | 希仁之拥的博客 马云宣布1年后卸任集团董事局主席 由CEO张勇接任 | 希仁之拥的博客 nginx配置非443端口的https连接 | 希仁之拥的博客 PHP7 异常类型列表 | 希仁之拥的博客 【分享】《燃点》:不被嘲笑的愿望是不值得去实现的|一部属于创业者的电影 | 希仁之拥的博客 【分享】前端资源加载优先级[译] | 希仁之拥的博客 【小技巧】Laravel动态配置多个数据库连接 | 希仁之拥的博客 php使用ftp上传、下载文件 | 希仁之拥的博客 php使用mysqlnd扩展进行mysql异步并行查询 | 希仁之拥的博客 基于以太坊区块链,使用web3.js在geth服务器上执行合约方法以及事件监听 | 希仁之拥的博客 Smartisan OS | 希仁之拥的博客 母校 | 希仁之拥的博客 php后期静态绑定 | 希仁之拥的博客 [mysql优化]子查询与连接查询 | 希仁之拥的博客 PHP常见运行模式及原理 | 希仁之拥的博客 mysql用户与授权 | 希仁之拥的博客 如何在ubuntu14.04上安装composer | 希仁之拥的博客 关于mews/captcha for laravel存在method [validateCaptcha] doesn't exists解决方法 | 希仁之拥的博客
由GORM的Updates语法糖
希仁之拥 · 2025-05-22 · via 希仁之拥

前言

在最近的项目中,同事使用GORM操作数据库,在更新数据的时候,发生了一些期望之外的效果,称之为“异常”或“bug”。经过对GORM官方文档的研读,发现了特有的提示,进而引发了我更进一步的思考。

经过

同事在进行常规的业务开发的时候,对数据库表记录进行更新,在操作的过程中,一个看似常规的操作,引起了数据被异常更新了。

下面是数据库表的模型字段定义

package models

import (
	"github.com/keepchen/go-sail/v3/orm"
	"github.com/shopspring/decimal"
)

// TransactionInfoList 流水表
type TransactionInfoList struct {
	orm.BaseModel
	RequestNo      string             `gorm:"column:request_no;type:varchar(64);NOT NULL;uniqueIndex:idx_requestNo;comment:请求订单号"`
	TradeNo        string             `gorm:"column:trade_no;type:varchar(64);default:'';comment:钱包订单号"`
	OrderNo        string             `gorm:"column:order_no;type:varchar(64);NOT NULL;index:idx_orderNo;comment:这笔交易对于的业务订单号"`
	Uid            string             `gorm:"column:uid;type:varchar(150);NOT NULL;index:idx_uid;comment:用户ID"`
	Message        string             `gorm:"column:message;type:varchar(128);default:'';comment:返回信息"`
	State          string             `gorm:"column:state;type:varchar(128);default:'';comment:回调状态"`
        其他字段已省略...
}

func (o *TransactionInfoList) TableName() string {
	return "transaction_info_list"
}

以下是go-sail的orm.BaseModel定义

package orm

import (
	"time"
)

// BaseModel 基础模型字段
//
// docs: https://gorm.io/docs/models.html
type BaseModel struct {
	ID        uint64     `gorm:"column:id;type:bigint UNSIGNED;primaryKey;AUTO_INCREMENT;comment:主键ID"`
	CreatedAt time.Time  `gorm:"column:created_at;type:datetime;comment:创建时间"`    //创建时间
	UpdatedAt time.Time  `gorm:"column:updated_at;type:datetime;comment:更新时间"`    //更新时间
	DeletedAt *time.Time `gorm:"column:deleted_at;type:datetime;comment:(软)删除时间"` //(软)删除时间
}

// NoneID 空ID
const NoneID = uint64(0)

var nowTimeFunc = func() time.Time {
	return time.Now()
}

// SetHookTimeFunc 设置时间勾子函数
func SetHookTimeFunc(ntf func() time.Time) {
	nowTimeFunc = ntf
}

同事执行的sql操作也是非常的简单,就是根据业务逻辑对交易状态进行更新:

...
tansactionInfo := &models.TransactionInfoList{State: "SUCCESS"}
err := sail.GetDBW().WithContext(ctx).Updates(tansactionInfo).Error 
...

起初还觉得一切正常,后面到深入自测以及测试同学介入的时候,发现创建时间字段被意外更新了,这显然是不合理的也是不应该出现的。同事本以为这是gorm的bug,去查阅gorm官方文档之后,发现官方文档有这么一句提示:

NOTE When updating with struct, GORM will only update non-zero fields. You might want to use map to update attributes or use Select to specify fields to update

也就是说,如果用结构体(struct)的方式进行数据更新操作,那么GORM只更新非零值

这里就引发了我的深入思考了,什么是非零值

回过头去看orm.BaseModel中的定义,created_at字段是time.Time类型,当没有对其进行赋值时,它的值是数据类型的默认值,也就是time.Time{}对象,此值是非零值。也就会触发GORM的更新行为。

那么再进一步思考,如何界定默认值赋予的默认值呢?

比如:

var age int

var age int = 0

前者是对变量的初始化行为,用户并没有赋值,后者是初始化并赋予用户值0。两者的值是相等的,这样的情况下,是无法区分默认值赋予的默认值的。

谈到这里,让我不禁想到了Java,这门表达能力极为丰富的老牌静态类型语言。就拿整型这个数据类型来讲,它有Integerint,看了一下Java对Integer的源码定义:

package java.lang;

import java.lang.annotation.Native;

public final class Integer extends Number implements Comparable<Integer> {
 ...
}

发现Intege是一个,那么对于变量声明的时候,意义就全然不同的了:

private Integer age;
private int age;

一个是基础数据类型,一个是类型。
我对Java并不了解,据我身边熟悉Java的朋友介绍,Integer是高级数据类型,会有装箱拆箱的操作。如果age没有被赋值,那么它将是null而不是0,因此可以通过这样的方式界定究竟是仅初始化(定义)还是赋予的默认值。
另外,在Java领域,有一个思想是变化是由行为触发的,动作/行为引发了变化,因此推崇setXX来触发值的变化,比如:

dto.setAge(1)

这样一来,是不是还能在setAge语法糖里面做更多的事情,比如代理、通知还有其他?

言归正传

要解决上面出现的问题,GORM官方文档也指出了解决方案,那就是使用Omit()显式指定忽略对某字段的更新:

...
tansactionInfo := &models.TransactionInfoList{State: "SUCCESS"}
err := sail.GetDBW().WithContext(ctx).Omit("created_at").Updates(tansactionInfo).Error 
...

这样问题是解决了,但是看起来总是不太优雅,还有别的方案吗?答案是肯定的。GORM在模型定义的时候,支持设置仅更新仅创建的权限操作。👉 文档

那么,我们对orm.BaseModel进行调整:

// BaseModel 基础模型字段
//
// docs: https://gorm.io/docs/models.html
type BaseModel struct {
	ID        uint64     `gorm:"column:id;type:bigint UNSIGNED;primaryKey;AUTO_INCREMENT;comment:主键ID"`
	CreatedAt time.Time  `gorm:"column:created_at;type:datetime;comment:创建时间;autoCreateTime;<-:create"` //创建时间
	UpdatedAt time.Time  `gorm:"column:updated_at;type:datetime;comment:更新时间;autoUpdateTime"`           //更新时间
	DeletedAt *time.Time `gorm:"column:deleted_at;type:datetime;comment:(软)删除时间"`                       //(软)删除时间
}

这样一来,就不用在每一处调用的地方都写Omit了。

胡思乱想

你们说,Golang有一天会出类似Integer这样的高级类型吗?或者说已经有第三方库在做这个事情了呢?


980_120.png(via stardots.io)

觉得博主写的不错?给他一个赞赏:

keepchen的赞赏码-支付宝 keepchen的赞赏码-微信