go - gorm使用过程中遇到的问题和解决

前言

考虑到gorm目前已经升级到了2.0版本, 且官方更新记录里罗列了若干升级后的优势, 故这里介绍的, 大部分都是基于gorm2.0

gorm1.0+ import的package为"github.com/jinzhu/gorm
gorm2.0 import的package为"gorm.io/gorm"

升级到2.0以后, 开发时console不输出执行的sql

旧版本中, 是有以下方法去控制sql是否输出

// LogMode set log mode, `true` for detailed logs, `false` for no log, default, will only print error logs
func (s *DB) LogMode(enable bool) *DB {
	if enable {
		s.logMode = 2
	} else {
		s.logMode = 1
	}
	return s
}

但是升级到新版本后, 发现*DB不再有LogMode方法, 如果需要在console中输出执行的sql, 需要调整连接数据库时的gorm.Config,

	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold:             200 * time.Millisecond, // 慢 SQL 阈值, 200ms
			LogLevel:                  logger.Info,            // 日志级别
			IgnoreRecordNotFoundError: true,                   // 忽略ErrRecordNotFound(记录未找到)错误
			Colorful:                  true,                   // 彩色打印
		},
	)

	DB, err = gorm.Open(mysql.New(mysql.Config{
		DSN: dsn,
		//更多mysql的配置可以到官方查看, 这里就不展开了
	}), &gorm.Config{
		Logger:                                   newLogger,
		DisableForeignKeyConstraintWhenMigrating: true,
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true, // 使用单数表名,启用该选项后,`Student` 表将是`student`, 禁用则为`students`
			//TablePrefix:   "pre_", // 表名前缀,`User`表为`pre_users`
			//NameReplacer:  strings.NewReplacer("AbcID", "Abcid"), // 在转为数据库名称之前,使用NameReplacer更改结构/字段名称。
		},
	})

此外, 这里可以看到, 旧版本的db.SingularTable(true)也调整到gorm.Config里面了
还有旧版本对于表明前缀的handler也调整到gorm.Config里面了

	gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
		return setting.DatabaseSetting.TablePrefix + defaultTableName
	}

时间默认值

当使用的gorm为 "github.com/jinzhu/gorm"时, 可通过以下方式, 实现插入数据前赋予默认值

db.Callback().Create().Replace("gorm:update_time_stamp", updateTimeStampWhileCreate)
func updateTimeStampWhileCreate(scope *gorm.Scope) {
	if !scope.HasError() {
		nowTime := time.Now()
		if createTimeField, ok := scope.FieldByName("CreateTime"); ok {
			if createTimeField.IsBlank {
				createTimeField.Set(nowTime)
			}
		}

		if updateTimeField, ok := scope.FieldByName("UpdateTime"); ok {
			if updateTimeField.IsBlank {
				updateTimeField.Set(nowTime)
			}
		}
	}
}

升级到 "gorm.io/gorm" 后, callback的参数不再是实体了, 而是DB, 故调整为:

	DB.Callback().Create().Before("gorm:create").Register("update_time_before_create", func(db *gorm.DB) {
		timeFieldsToInit := []string{"CreateTime", "UpdateTime"}
		for _, field := range timeFieldsToInit {
			if timeField := db .Statement.Schema.LookUpField(field); timeField != nil {
				fmt.Println(field, timeField)
				timeField.Set(db .Statement.ReflectValue, time.Now())
			}
		}
	})

如果Create单个数据的时候, 这样操作还是没问题的, 但是, 如果是执行批量操作, 这样明显就不行了, 需要在这里加个判断, 判断db .Statement.ReflectValue的类型

	DB.Callback().Create().Before("gorm:create").Register("update_create_time", func(db *gorm.DB) {
		timeFieldsToInit := []string{"CreateTime", "UpdateTime"}
		for _, field := range timeFieldsToInit {

			if timeField := db.Statement.Schema.LookUpField(field); timeField != nil {
				switch db.Statement.ReflectValue.Kind() {
				case reflect.Slice, reflect.Array:
					for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
						timeField.Set(db.Statement.ReflectValue.Index(i), time.Now())
					}
				case reflect.Struct:
					timeField.Set(db.Statement.ReflectValue, time.Now())
				}
			}
		}
	})

但是这样写有个不好的地方是, 已经赋值了的会被覆盖掉, 故, 上面缺少对原值的判断, 需要加上:

	DB.Callback().Create().Before("gorm:create").Register("update_create_time", func(db *gorm.DB) {
		timeFieldsToInit := []string{"CreateTime", "UpdateTime"}
		for _, field := range timeFieldsToInit {

			if timeField := db.Statement.Schema.LookUpField(field); timeField != nil {
				switch db.Statement.ReflectValue.Kind() {
				case reflect.Slice, reflect.Array:
					for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
						if _, isZero := timeField.ValueOf(db.Statement.ReflectValue.Index(i)); isZero {
							timeField.Set(db.Statement.ReflectValue.Index(i), time.Now())
						}
					}
				case reflect.Struct:
					if _, isZero := timeField.ValueOf(db.Statement.ReflectValue); isZero {
						timeField.Set(db.Statement.ReflectValue, time.Now())
					}
				}
			}
		}
	})

升级到某个版本后(我是升级到1.23.6), 需使用context

// 可以从当前 Statement中访问 Context 对象
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  ctx := tx.Statement.Context
  // ...
  return
}

详见在 Hooks/Callbacks 中使用 Context

更多callback的使用, 可见: gorm-编写插件

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值