79374955

Date: 2025-01-21 15:19:55
Score: 1
Natty:
Report link

Your thinking is correct.

These are the Available hooks for updating:

// begin transaction
BeforeSave
BeforeUpdate
// save before associations
// update database
// save after associations
AfterUpdate
AfterSave
// commit or rollback transaction

And they work as expected. Check this example, I'll use SQLite for simplicity:

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/google/uuid"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
)

type Player struct {
    ID     *uuid.UUID `gorm:"type:uuid;primaryKey;" json:"ID"`
    TeamID *uuid.UUID `gorm:"type:uuid" json:"TeamID"`
    Name   string     `json:"Name"`
    gorm.Model
}

type Team struct {
    ID      *uuid.UUID `gorm:"type:uuid;primaryKey;" json:"ID"`
    Players []Player   `gorm:"foreignKey:TeamID;references:ID" json:"Players"`
    Name    string     `json:"Name"`
    gorm.Model
}

func (player *Player) BeforeUpdate(tx *gorm.DB) (err error) {
    if player.TeamID != nil && *player.TeamID != uuid.Nil {
        tx.Model(&Team{}).Where("id = ?", *player.TeamID).Update("updated_at", time.Now())
    }

    return
}

func main() {
    newLogger := logger.New(
        log.New(log.Writer(), "\r\n", log.LstdFlags),
        logger.Config{
            SlowThreshold:             time.Second,
            LogLevel:                  logger.Info,
            IgnoreRecordNotFoundError: true,
            Colorful:                  true,
        },
    )

    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
        Logger: newLogger,
    })
    if err != nil {
        log.Fatalf("failed to connect database: %v", err)
    }

    if err := db.AutoMigrate(&Team{}, &Player{}); err != nil {
        log.Fatalf("failed to migrate database: %v", err)
    }

    teamID := uuid.New()
    team := Team{
        ID:   &teamID,
        Name: "Team A",
    }

    if err := db.Create(&team).Error; err != nil {
        log.Fatalf("failed to create team: %v", err)
    }

    playerID := uuid.New()
    player := Player{
        ID:     &playerID,
        TeamID: &teamID,
        Name:   "Player 1",
    }

    if err := db.Create(&player).Error; err != nil {
        log.Fatalf("failed to create player: %v", err)
    }

    fmt.Printf("Initial UpdatedAt - Team: %v, Player: %v\n", team.UpdatedAt, player.UpdatedAt)

    time.Sleep(2 * time.Second)

    if err := db.Model(&player).Update("Name", "Updated Player 1").Error; err != nil {
        log.Fatalf("failed to update player: %v", err)
    }

    var updatedTeam Team
    if err := db.First(&updatedTeam, "id = ?", teamID).Error; err != nil {
        log.Fatalf("failed to retrieve team: %v", err)
    }

    var updatedPlayer Player
    if err := db.First(&updatedPlayer, "id = ?", playerID).Error; err != nil {
        log.Fatalf("failed to retrieve player: %v", err)
    }

    fmt.Printf("Updated UpdatedAt - Team: %v, Player: %v\n", updatedTeam.UpdatedAt, updatedPlayer.UpdatedAt)
}

The Logger is enabled to show all raw SQL sent to the database. When you run:

go run main.go 

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:54
[0.030ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="teams"

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:54
[0.061ms] [rows:2] SELECT sql FROM sqlite_master WHERE type IN ("table","index") AND tbl_name = "teams" AND sql IS NOT NULL order by type = "table" desc

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:54
[0.018ms] [rows:-] SELECT * FROM `teams` LIMIT 1

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:54
[0.017ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type = "index" AND tbl_name = "teams" AND name = "idx_teams_deleted_at"

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:54
[0.014ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="players"

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:54
[0.045ms] [rows:2] SELECT sql FROM sqlite_master WHERE type IN ("table","index") AND tbl_name = "players" AND sql IS NOT NULL order by type = "table" desc

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:54
[0.015ms] [rows:-] SELECT * FROM `players` LIMIT 1

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:54
[0.027ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type = "table" AND tbl_name = "players" AND (sql LIKE "%CONSTRAINT ""fk_teams_players"" %" OR sql LIKE "%CONSTRAINT fk_teams_players %" OR sql LIKE "%CONSTRAINT `fk_teams_players`%" OR sql LIKE "%CONSTRAINT [fk_teams_players]%" OR sql LIKE "%CONSTRAINT    fk_teams_players    %")

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:54
[0.016ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type = "index" AND tbl_name = "players" AND name = "idx_players_deleted_at"

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:64
[2.639ms] [rows:1] INSERT INTO `teams` (`id`,`name`,`created_at`,`updated_at`,`deleted_at`) VALUES ("3b2fc6a1-54ee-417b-9959-8849d6a9e403","Team A","2025-01-21 12:05:17.013","2025-01-21 12:05:17.013",NULL) RETURNING `id`

2025/01/21 12:05:17 /home/wilton/Workspace/gorm/main.go:75
[2.272ms] [rows:1] INSERT INTO `players` (`id`,`team_id`,`name`,`created_at`,`updated_at`,`deleted_at`) VALUES ("fbb3a88c-d6fa-4181-bfd7-8803ceb9ba69","3b2fc6a1-54ee-417b-9959-8849d6a9e403","Player 1","2025-01-21 12:05:17.016","2025-01-21 12:05:17.016",NULL) RETURNING `id`
Initial UpdatedAt - Team: 2025-01-21 12:05:17.013606731 -0300 -03, Player: 2025-01-21 12:05:17.016384285 -0300 -03

2025/01/21 12:05:19 /home/wilton/Workspace/gorm/main.go:30
[0.352ms] [rows:1] UPDATE `teams` SET `updated_at`="2025-01-21 12:05:19.019" WHERE id = "3b2fc6a1-54ee-417b-9959-8849d6a9e403" AND `teams`.`deleted_at` IS NULL

2025/01/21 12:05:19 /home/wilton/Workspace/gorm/main.go:83
[2.770ms] [rows:1] UPDATE `players` SET `name`="Updated Player 1",`updated_at`="2025-01-21 12:05:19.02" WHERE `players`.`deleted_at` IS NULL AND `id` = "fbb3a88c-d6fa-4181-bfd7-8803ceb9ba69"

2025/01/21 12:05:19 /home/wilton/Workspace/gorm/main.go:88
[0.258ms] [rows:1] SELECT * FROM `teams` WHERE id = "3b2fc6a1-54ee-417b-9959-8849d6a9e403" AND `teams`.`deleted_at` IS NULL ORDER BY `teams`.`id` LIMIT 1

2025/01/21 12:05:19 /home/wilton/Workspace/gorm/main.go:93
[0.159ms] [rows:1] SELECT * FROM `players` WHERE id = "fbb3a88c-d6fa-4181-bfd7-8803ceb9ba69" AND `players`.`deleted_at` IS NULL ORDER BY `players`.`id` LIMIT 1
Updated UpdatedAt - Team: 2025-01-21 12:05:19.019831936 -0300 -0300, Player: 2025-01-21 12:05:19.020259014 -0300 -0300

Note the UPDATE statements after waiting for 2 seconds. It was corrected sent before the update since you are using the BeforeUpdate hook.

2025/01/21 12:05:19 /home/wilton/Workspace/gorm/main.go:30
[0.352ms] [rows:1] UPDATE `teams` SET `updated_at`="2025-01-21 12:05:19.019" WHERE id = "3b2fc6a1-54ee-417b-9959-8849d6a9e403" AND `teams`.`deleted_at` IS NULL

2025/01/21 12:05:19 /home/wilton/Workspace/gorm/main.go:83
[2.770ms] [rows:1] UPDATE `players` SET `name`="Updated Player 1",`updated_at`="2025-01-21 12:05:19.02" WHERE `players`.`deleted_at` IS NULL AND `id` = "fbb3a88c-d6fa-4181-bfd7-8803ceb9ba69"

Could you test this in your environment and check if you can reproduce the results?

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • Ends in question mark (2):
  • Low reputation (0.5):
Posted by: wiltonsr