ITADN

Breaking change: `Raw().Scan()` resets non-selected struct fields to zero values in v1.31.1

#7746Opensuperpluss 创建于 2026-04-06
type:question
S
superplusscommented
## Description ### Summary Upgrading `gorm.io/gorm` from **v1.25.5** to **v1.31.1** introduces a breaking change in `Raw().Scan()` behavior when scanning into a struct that already contains pre-filled values. ### Expected Behavior When executing: ```go err := db.Raw(`SELECT id FROM user WHERE id = 1;`).Scan(&user).Error ``` Only the selected fields (`id`) should be updated in the destination struct. Other fields should retain their original values. Result in **v1.25.5**: ```json {"ID":1,"Account":"100","Password":"100","Salt":"100","PasswordStrength":100} ``` ### Actual Behavior In **v1.31.1**, fields that are not selected in the query are reset to their zero values. Result: ```json {"ID":1,"Account":"","Password":"","Salt":"","PasswordStrength":0} ``` ### Impact This change breaks backward compatibility and affects existing codebases that rely on partial field updates using `Raw().Scan()`. ### Reproducible Example ```go package main import ( "encoding/json" "log" "os" "time" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" "gorm.io/gorm/schema" ) var ( db *gorm.DB ) func init() { var err error db, err = gorm.Open(mysql.Open(`root:******@tcp(******:3306)/account?charset=utf8mb4&parseTime=True`), &gorm.Config{ NamingStrategy: schema.NamingStrategy{ SingularTable: true, }, Logger: logger.New( log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{ SlowThreshold: time.Second, LogLevel: logger.Info, IgnoreRecordNotFoundError: false, ParameterizedQueries: false, Colorful: true, }, ), DisableForeignKeyConstraintWhenMigrating: true, }) if err != nil { log.Println(err) return } } type User struct { ID uint `gorm:"column:id"` Account string `gorm:"column:account"` Password string `gorm:"column:password"` Salt string `gorm:"column:salt"` PasswordStrength int8 `gorm:"-"` } func (m *User) TableName() string { return "user" } func main() { user := User{ ID: 100, Account: "100", Password: "100", Salt: "100", PasswordStrength: 100, } err := db.Raw(`SELECT id FROM user WHERE id = 1;`).Scan(&user).Error if err != nil { log.Println(err) return } userBytes, err := json.Marshal(&user) if err != nil { log.Println(err) return } log.Println(string(userBytes)) } ``` ### Questions 1. Is this behavior change intentional? 2. If yes, is there a recommended way to preserve existing struct values when scanning partial columns? 3. If not intentional, could this be considered a regression? ### Additional Notes This behavior change can silently introduce data loss in application logic where partial field queries are expected to be non-destructive.
1 条评论