Your Question

Does golang Gorm 2.0 support a global BeforeSave hook function? I have attempted many scenarios, and one of my attempts is outlined below, but it failed. The SQL date still remains as '0000-00-00'.

Please provide an example of globally registering a hook function. Thank you very much.

The document you expected this should be explained

https://gorm.io/docs/hooks.html

Expected answer

type CreateUpdate struct {
    CreatedBy   uint      `json:"created_by"`
    CreatedTime time.Time `json:"created_time"`
    UpdatedBy   uint      `json:"updated_by"`
    UpdatedTime time.Time `json:"updated_time"`
}

type UserInfo struct {
    fmkModel.ID
    Name     string `json:"name" gorm:"not null;comment:用户名称"`
    Mobile   string `json:"mobile" gorm:"not null;index;comment:用户手机号"`
    Password string `json:"password" gorm:"not null;default:'';comment:用户密码"`
    fmkModel.CreateUpdate
    fmkModel.SoftDeletes
}

// Register global Hook
DB.Callback().Create().Before("gorm:before_save").Register("my:before_save", bootstrap.MyBeforeSaveHook)

func MyBeforeSaveHook(db *gorm.DB) {
    statement := db.Statement
    setGormCreateTime(statement, nowTime)
    setGormUpdateTime(statement, nowTime)

}

func setGormCreateTime(statement *gorm.Statement, nowTime time.Time) {
    dest := statement.Dest

    value := reflect.ValueOf(dest)
    //value  &{{0 0001-01-01 00:00:00 +0000 UTC 0 0001-01-01 00:00:00 +0000 UTC} 111111}
    fmt.Println("setGormCreateTime value ", value)
    valueElem := value.Elem()
    //valueElem  {{0 0001-01-01 00:00:00 +0000 UTC 0 0001-01-01 00:00:00 +0000 UTC} 111111}
    fmt.Println("setGormCreateTime valueElem ", valueElem)

    createUpdateField := valueElem.FieldByName("CreateUpdate")
    if !createUpdateField.IsValid() {
        fmt.Println("setGormCreateTime createUpdateValue 是不合法的")
        return
    }
    fmt.Println("setGormCreateTime createUpdateValue ", createUpdateField.Kind())
    if reflect.Struct != createUpdateField.Kind() {
        return
    }
    createdTimeField := createUpdateField.FieldByName("CreatedTime")
    // Check if the CreatedTime field is valid and can be set
    if createdTimeField.IsValid() && createdTimeField.CanSet() {
        // Modify the CreatedTime value
        //createdTimeField.Set(reflect.ValueOf(newTime))
        statement.SetColumn("CreatedTime", nowTime)
    }
}

I can see that the time in 'dest' has been updated to the current time. However, when executing the SQL, an error still occurs.

[5567.310ms] [rows:0] INSERT INTO `user_info` (`name`,`mobile`,`password`,`created_by`,`created_time`,`updated_by`,`updated_time`,`del_flag`) VALUES ('张三','18000855903','$2a$04$VfAU4pidT6wI6llBQKT1GuEcd2bmFfnbuSu.YprgXHYwA7Nl5FoJ.',0,'0000-00-00 00:00:00',0,'0000-00-00 00:00:00',0)
[2023-12-08 01:01:05.427]   local.error controller/userInfoController.go:21 Error 1292 (22007): Incorrect datetime value: '0000-00-00' for column 'created_time' at row 1


Comment From: WarriorFromLongAgo

I also tried a solution, but it failed.

    sch := db.Statement.Schema
    value, ok := sch.FieldsByDBName["created_time"]
    if ok {
        fieldType := value.FieldType.String()
        if fieldType == "time.Time" {
            err := value.Set(db.Statement.Context, db.Statement.ReflectValue, time.Now())
            if err != nil {
                return
            }
        }
    }


Comment From: WarriorFromLongAgo

https://github.com/WarriorFromLongAgo/xue-gao-write-and-use/blob/golang_micro_gin_v1/gin20231203_1612_v1/bootstrap/db.go MyBeforeSaveHook

Comment From: WarriorFromLongAgo

The problem has been resolved.

change before: DB.Callback().Create().Before("gorm:before_save").Register("my:before_save", bootstrap.MyBeforeSaveHook)

change after: DB.Callback().Create().Before("gorm:before_create").Register("my:before_create", bootstrap.MyBeforeSaveHook)