## GORM 字段名映射规则 这实际上是由于 **GORM 字段名映射** 导致的。 在你的主模型结构体 `TVipOrders` 中,你为字段指定了自定义的数据库列名,例如: ```go:internal/model/t_vip_orders.go type TVipOrders struct { // ... TradeState string `json:"tradeState" gorm:"column:tradeState;comment:交易状态"` // ... UpdateTime *time.Time `json:"updateTime" gorm:"column:updateTime;comment:更新时间"` // ... } ``` 而在更新时使用的结构体 `VIPOrderUpdate` 没有指定这些 gorm 标签,定义如下: ```go type VIPOrderUpdate struct { Status int TradeState string PrepayId string Phone string Openid string UpdateTime time.Time } ``` 当你调用 ```go db.Model(&TVipOrders{}).Where("orderId = ?", orderId).Updates(update) ``` 时,GORM 会对传入的 `update` 结构体进行字段扫描并采用默认的命名策略(即将字段名转换为蛇形格式): - `Status` 映射为 `"status"` - `TradeState` 映射为 `"trade_state"` - `UpdateTime` 映射为 `"update_time"` 此时,由于你的模型 `TVipOrders` 的字段对应的数据库列分别定义为 `status`(匹配)、`tradeState`(**不匹配** `"trade_state"`)和 `updateTime`(**不匹配** `"update_time"`),所以只有 `Status` 字段能正确更新,而其他字段就会被忽略。 --- ### 解决方案 有两种常见的改法: 1. **为更新结构体添加明确的 gorm 标签**,使其列名与模型中保持一致。例如,在 `VIPOrderUpdate` 上增加标签: ```go type VIPOrderUpdate struct { Status int `gorm:"column:status"` TradeState string `gorm:"column:tradeState"` PrepayId string `gorm:"column:prepayId"` Phone string `gorm:"column:phone"` Openid string `gorm:"column:openid"` UpdateTime time.Time `gorm:"column:updateTime"` } ``` 这样,当调用 `Updates(update)` 时,GORM 就会生成类似下面的 SQL: ``` UPDATE `t_vip_orders` SET `status`=300, `tradeState`='SUCCESS', `updateTime`='2025-02-17T16:28:35...' WHERE orderId = 79 ``` 2. **使用 map 来更新字段,确保 map 中键的名称正好匹配数据库列名称**。例如: ```go db.Model(&TVipOrders{}).Where("orderId = ?", orderId).Updates(map[string]interface{}{ "status": model.OrderStatusOpenVIPSuccess, "tradeState": "SUCCESS", "updateTime": time.Now(), }) ``` 两种方式都能确保你传入的字段名和数据库表中定义的列名一致,从而让所有你想更新的字段都被正确更新。 --- ### 总结 你的代码中虽然在构造 update 对象时确实设置了 `UpdateTime` 为 `time.Now()` 和 `TradeState` 为 `notifyData.TradeState`(即 "SUCCESS"),但由于 `VIPOrderUpdate` 结构体没有指定正确的列名,GORM 按照默认规则将它们映射为 `update_time` 和 `trade_state`,从而没有匹配到表中实际的 `updateTime` 和 `tradeState` 字段,最终只更新了 `status`。 希望这能帮你理解问题的原因并提供解决方案。 ## 数据库操作封装 这段代码是用Go语言编写的,使用了GORM库来进行数据库操作。以下是对代码的详细解释: ```go // accessor 数据库交互封装层 type accessor struct { db *gorm.DB } // NewAccessor 创建一个新的accessor实例 func NewAccessor(DB *gorm.DB) Accessor { return &accessor{db: DB} } // DB 返回当前的数据库实例 func (u *accessor) DB() *gorm.DB { return u.db } // Begin 开始一个新的数据库事务 func (u *accessor) Begin() *gorm.DB { return u.db.Begin() } // First 根据查询条件获取第一条记录 func (u *accessor) First(ctx context.Context, m, query interface{}, args ...interface{}) error { db := u.db if ctx != nil { db = mysql.WithContext(ctx, db) } if query != nil && args != nil { db = db.Where(query, args...) } if err := db.First(m).Error; err != nil { return errors.Wrap(err, "get error") } return nil } ``` ### 代码解析 1. **结构体定义**: ```go type accessor struct { db *gorm.DB } ``` `accessor`结构体封装了一个GORM的数据库实例`db`。 2. **构造函数**: ```go func NewAccessor(DB *gorm.DB) Accessor { return &accessor{db: DB} } ``` `NewAccessor`函数用于创建一个新的`accessor`实例,并将传入的数据库实例`DB`赋值给`accessor`的`db`字段。 3. **获取数据库实例**: ```go func (u *accessor) DB() *gorm.DB { return u.db } ``` `DB`方法返回当前`accessor`实例中的数据库实例。 4. **开始事务**: ```go func (u *accessor) Begin() *gorm.DB { return u.db.Begin() } ``` `Begin`方法开始一个新的数据库事务,并返回事务实例。 5. **查询第一条记录**: ```go func (u *accessor) First(ctx context.Context, m, query interface{}, args ...interface{}) error { db := u.db if ctx != nil { db = mysql.WithContext(ctx, db) } if query != nil && args != nil { db = db.Where(query, args...) } if err := db.First(m).Error; err != nil { return errors.Wrap(err, "get error") } return nil } ``` `First`方法根据查询条件获取第一条记录。它接受一个上下文`ctx`、一个模型`m`、查询条件`query`和可变参数`args`。如果上下文不为空,则将上下文与数据库实例关联;如果查询条件和参数不为空,则应用查询条件。最后,执行`First`方法获取第一条记录,如果有错误则返回包装后的错误信息。 ### 注意事项 - `mysql.WithContext(ctx, db)` 这个方法假设是一个自定义的方法,用于将上下文与数据库实例关联。需要确保在代码中有相应的实现。 - 错误处理使用了`errors.Wrap`,这需要引入`github.com/pkg/errors`包。 如果有任何具体问题或需要进一步的解释,请告诉我! Loading... ## GORM 字段名映射规则 这实际上是由于 **GORM 字段名映射** 导致的。 在你的主模型结构体 `TVipOrders` 中,你为字段指定了自定义的数据库列名,例如: ```go:internal/model/t_vip_orders.go type TVipOrders struct { // ... TradeState string `json:"tradeState" gorm:"column:tradeState;comment:交易状态"` // ... UpdateTime *time.Time `json:"updateTime" gorm:"column:updateTime;comment:更新时间"` // ... } ``` 而在更新时使用的结构体 `VIPOrderUpdate` 没有指定这些 gorm 标签,定义如下: ```go type VIPOrderUpdate struct { Status int TradeState string PrepayId string Phone string Openid string UpdateTime time.Time } ``` 当你调用 ```go db.Model(&TVipOrders{}).Where("orderId = ?", orderId).Updates(update) ``` 时,GORM 会对传入的 `update` 结构体进行字段扫描并采用默认的命名策略(即将字段名转换为蛇形格式): - `Status` 映射为 `"status"` - `TradeState` 映射为 `"trade_state"` - `UpdateTime` 映射为 `"update_time"` 此时,由于你的模型 `TVipOrders` 的字段对应的数据库列分别定义为 `status`(匹配)、`tradeState`(**不匹配** `"trade_state"`)和 `updateTime`(**不匹配** `"update_time"`),所以只有 `Status` 字段能正确更新,而其他字段就会被忽略。 --- ### 解决方案 有两种常见的改法: 1. **为更新结构体添加明确的 gorm 标签**,使其列名与模型中保持一致。例如,在 `VIPOrderUpdate` 上增加标签: ```go type VIPOrderUpdate struct { Status int `gorm:"column:status"` TradeState string `gorm:"column:tradeState"` PrepayId string `gorm:"column:prepayId"` Phone string `gorm:"column:phone"` Openid string `gorm:"column:openid"` UpdateTime time.Time `gorm:"column:updateTime"` } ``` 这样,当调用 `Updates(update)` 时,GORM 就会生成类似下面的 SQL: ``` UPDATE `t_vip_orders` SET `status`=300, `tradeState`='SUCCESS', `updateTime`='2025-02-17T16:28:35...' WHERE orderId = 79 ``` 2. **使用 map 来更新字段,确保 map 中键的名称正好匹配数据库列名称**。例如: ```go db.Model(&TVipOrders{}).Where("orderId = ?", orderId).Updates(map[string]interface{}{ "status": model.OrderStatusOpenVIPSuccess, "tradeState": "SUCCESS", "updateTime": time.Now(), }) ``` 两种方式都能确保你传入的字段名和数据库表中定义的列名一致,从而让所有你想更新的字段都被正确更新。 --- ### 总结 你的代码中虽然在构造 update 对象时确实设置了 `UpdateTime` 为 `time.Now()` 和 `TradeState` 为 `notifyData.TradeState`(即 "SUCCESS"),但由于 `VIPOrderUpdate` 结构体没有指定正确的列名,GORM 按照默认规则将它们映射为 `update_time` 和 `trade_state`,从而没有匹配到表中实际的 `updateTime` 和 `tradeState` 字段,最终只更新了 `status`。 希望这能帮你理解问题的原因并提供解决方案。 ## 数据库操作封装 这段代码是用Go语言编写的,使用了GORM库来进行数据库操作。以下是对代码的详细解释: ```go // accessor 数据库交互封装层 type accessor struct { db *gorm.DB } // NewAccessor 创建一个新的accessor实例 func NewAccessor(DB *gorm.DB) Accessor { return &accessor{db: DB} } // DB 返回当前的数据库实例 func (u *accessor) DB() *gorm.DB { return u.db } // Begin 开始一个新的数据库事务 func (u *accessor) Begin() *gorm.DB { return u.db.Begin() } // First 根据查询条件获取第一条记录 func (u *accessor) First(ctx context.Context, m, query interface{}, args ...interface{}) error { db := u.db if ctx != nil { db = mysql.WithContext(ctx, db) } if query != nil && args != nil { db = db.Where(query, args...) } if err := db.First(m).Error; err != nil { return errors.Wrap(err, "get error") } return nil } ``` ### 代码解析 1. **结构体定义**: ```go type accessor struct { db *gorm.DB } ``` `accessor`结构体封装了一个GORM的数据库实例`db`。 2. **构造函数**: ```go func NewAccessor(DB *gorm.DB) Accessor { return &accessor{db: DB} } ``` `NewAccessor`函数用于创建一个新的`accessor`实例,并将传入的数据库实例`DB`赋值给`accessor`的`db`字段。 3. **获取数据库实例**: ```go func (u *accessor) DB() *gorm.DB { return u.db } ``` `DB`方法返回当前`accessor`实例中的数据库实例。 4. **开始事务**: ```go func (u *accessor) Begin() *gorm.DB { return u.db.Begin() } ``` `Begin`方法开始一个新的数据库事务,并返回事务实例。 5. **查询第一条记录**: ```go func (u *accessor) First(ctx context.Context, m, query interface{}, args ...interface{}) error { db := u.db if ctx != nil { db = mysql.WithContext(ctx, db) } if query != nil && args != nil { db = db.Where(query, args...) } if err := db.First(m).Error; err != nil { return errors.Wrap(err, "get error") } return nil } ``` `First`方法根据查询条件获取第一条记录。它接受一个上下文`ctx`、一个模型`m`、查询条件`query`和可变参数`args`。如果上下文不为空,则将上下文与数据库实例关联;如果查询条件和参数不为空,则应用查询条件。最后,执行`First`方法获取第一条记录,如果有错误则返回包装后的错误信息。 ### 注意事项 - `mysql.WithContext(ctx, db)` 这个方法假设是一个自定义的方法,用于将上下文与数据库实例关联。需要确保在代码中有相应的实现。 - 错误处理使用了`errors.Wrap`,这需要引入`github.com/pkg/errors`包。 如果有任何具体问题或需要进一步的解释,请告诉我! 最后修改:2025 年 02 月 19 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏