gorm框架常见问题
# gorm框架常见问题
本文总结golang的orm框架gorm在使用过程中的常见问题。
# 更新数据时,前端传入的参数为空的字段
没有更新到数据库
因为更新时,若输入参数类型为struct
, 则Updates方法只会更新非空白字段.
为了解决这个可以使用map作为参数类型. 示例代码如下:
为struct类型添加tag:
type Distributor struct {
model.BaseModel
//标题
Title string `structToMapForUpdate:"title"`
//编码
Code string `structToMapForUpdate:"-"`
//广告位
AdPos string `structToMapForUpdate:"ad_pos"`
//简介
Summary string `structToMapForUpdate:"summary"`
//状态:1启用,2禁用
Status int `structToMapForUpdate:"status"`
}
将struct对象按标签的规则转换为map对象, 然后使用map对象作为updates的输入参数即可.
//执行gorm的update方法时,若传入struct类型,那么空值不会做更新,所以此处需要转为map
distributorMap := gconv.Map(distributor, "structToMapForUpdate")
if err = d_distributor.DistributorDao.Update(dao.Db.Model(&m_distributor.Distributor{}).Omit("created_at").Where("id = ?", distributor.ID), distributorMap); err != nil {
log.Error(ctx, "修改渠道, 失败", zap.Any("distributor", distributor), zap.Error(err))
return err
}
另外一种方法: 将字段类型定义为指针,也可以解决零值问题。
# gorm做关联查询
使用gorm做关联查询时,需要考虑如下几点:
- 若查询的是个列表数据,需要考虑按哪个字段排序
- 注意关联的其他表需要手动过滤掉删除的数据。因为默认情况下,gorm并不会自动增加deleted_at过滤条件。
- 需要注意主表中的1条数据是否会关联副表的多条记录,若是,则要考虑去重。
# 定义json类型字段
若某个字段对应数据库表的某个json类型字段,那么把model中对应字段的类型定义为自定义类型,并且实现driver.Valuer接口
和sql.Scanner接口
【即为自定义类型定义2个方法,分别为Value
和Scan
】即可。
注意: Value
和Scan
的接收者类型需要和字段的类型保持一致。要么都是自定义类型,要么都是自定义类型的指针。
# 示例1【接收者为自定义类型,Scan无效,废弃方案】
type FuwuhaoUser struct {
ID uint64 `gorm:"column:id;primary_key;"` // 主键ID
Tagids TagIds `gorm:"column:tagids;NOT NULL"` // 用户被打上的标签ID列表
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;NOT NULL"` // 创建时间
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;NOT NULL"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at"` // 删除时间
}
type TagIds []int32
// Value :
func (m TagIds) Value() (driver.Value, error) {
str, err := json.Marshal(m)
if err != nil {
log.Errorf("%v", err)
return nil, err
}
log.Infof("tagIds", string(str))
return json.Marshal(m)
}
// Scan :
func (m TagIds) Scan(input interface{}) error {
if input == nil {
m = nil
return nil
}
bytes, ok := input.([]byte)
if !ok {
return errors.New(fmt.Sprint("Failed to unmarshal JSON value:", input))
}
return json.Unmarshal(bytes, &m)
}
# 示例2【接收者为自定义类型的指针】
type FuwuhaoUser struct {
ID uint64 `gorm:"column:id;primary_key;"` // 主键ID
Tagids *TagIds `gorm:"column:tagids;NOT NULL"` // 用户被打上的标签ID列表
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;NOT NULL"` // 创建时间
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;NOT NULL"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at"` // 删除时间
}
type TagIds []int32
/**
实现了driver.Valuer接口。
Value则由Go转到DB时被调用. 也就是说insert/update时候用
*/
func (m *TagIds) Value() (driver.Value, error) {
return json.Marshal(m)
}
/**
实现sql.Scanner接口。
Scan用于数据从DB转到Go时被调用,也就是说select时用。
接收者类型必须是指针类型,否则无法将json.Unmarshal的转换结果反映在当前字段上
*/
func (m *TagIds) Scan(input interface{}) error {
if input == nil {
m = nil
return nil
}
bytes, ok := input.([]byte)
if !ok {
return errors.New(fmt.Sprint("Failed to unmarshal JSON value:", input))
}
return json.Unmarshal(bytes, m)
}
# 示例1和示例2的区别
区别有3处:
- 字段类型不同
Value
和Scan
的接收者类型不同Scan
方法的json.Unmarshal的第2个参数的类型不同,需要做根据接收者类型做适配- 示例2存在的问题
当做查询操作时,虽然触发了scan方法,但是没有将查询结果反映在对应的成员变量上,原因是接收者类型不是指针引起。
上次更新: 2022-01-04 11:21:17