51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

gorm框架常见问题

# 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做关联查询时,需要考虑如下几点:

  1. 若查询的是个列表数据,需要考虑按哪个字段排序
  2. 注意关联的其他表需要手动过滤掉删除的数据。因为默认情况下,gorm并不会自动增加deleted_at过滤条件。
  3. 需要注意主表中的1条数据是否会关联副表的多条记录,若是,则要考虑去重。

# 定义json类型字段 {#定义json类型字段}

若某个字段对应数据库表的某个json类型字段,那么把model中对应字段的类型定义为自定义类型,并且实现driver.Valuer接口sql.Scanner接口【即为自定义类型定义2个方法,分别为ValueScan】即可。
注意: ValueScan的接收者类型需要和字段的类型保持一致。要么都是自定义类型,要么都是自定义类型的指针。

# 示例1【接收者为自定义类型,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【接收者为自定义类型的指针】 {#示例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的区别 {#示例1和示例2的区别}

区别有3处:

  • 字段类型不同
  • ValueScan的接收者类型不同
  • Scan方法的json.Unmarshal的第2个参数的类型不同,需要做根据接收者类型做适配
  • 示例2存在的问题
    当做查询操作时,虽然触发了scan方法,但是没有将查询结果反映在对应的成员变量上,原因是接收者类型不是指针引起。
赞(3)
未经允许不得转载:工具盒子 » gorm框架常见问题