Eric L.
Eric L.

Reputation: 237

How to prevent gorm from ignoring values for the `UpdatedAt` field in the .Updates() clause?

I have a MySQL table that has an updated_at timestamp column with not null default current_timestamp on update current_timestamp properties in its schema. I have tested the schema manually in MySQL workbench using UPDATE queries, and I learned that only when the updated_at column is not supplied with a specific value, then the on update current_timestamp property will kick in; when a specific value is supplied, however, the supplied value will be saved. This is the desired behavior.

Moving on, I use gorm to handle DAO stuff in my go project. For updating rows in this table, everything works super well, until I need to implement a method that only updates the last_login timestamp column without updating the updated_at column.

For some reason (maybe the fact that I set on update current_timestamp in the schema), the actual SQL generated by gorm will always SET updated_at in an UPDATE query no matter what. So, I figured that to prevent an update, maybe I should supply a timestamp value retrieved from a SELECT query beforehand (together in a transaction). But unfortunately, even though I have supplied a specific value to gorm's Updates() method, gorm ignores it, and updates the column to use the latest timestamp any way.

The go code in the DAO update method looks like this:

err := db.Model(&user).
    Omit("user_id").
    Updates(user).
    Error

And the user struct looks like this:

type User struct {
    UserID    uint64 `gorm:"primaryKey"`
    LastLogin *time.Time
    CreatedAt time.Time
    UpdatedAt time.Time
}

The LastLogin field takes a pointer to allow setting null value in MySQL; any way, this field doesn't have issues.

The update code roughly looks like this:

// retrieve current user row
found, err := s.DAO.FindUserByID(ctx, accountID, userID)
if err != nil {
    return err
}

// create a struct with existing UpdatedAt value
user := &User{
    UserID:    userID,
    LastLogin: lastLogin,
    UpdatedAt: found.UpdatedAt, // trying to prevent defaulting to current timestamp
}

// call the DAO update method
err = s.DAO.UpdateUser(ctx, *user)
return err

Despite having explicitly set the value for UpdatedAt, gorm ignores it and put the current timestamp in the generated SQL query instead.

How can I fix this?

Upvotes: 2

Views: 6139

Answers (1)

Ezequiel Muns
Ezequiel Muns

Reputation: 7742

There is a way.

Gorm's Update/Updates method will ignore any value you pass in for UpdatedAt and use the current timestamp instead. This is by design.

You can get gorm to not update the UpdatedAt field by using Omit("UpdatedAt"). However, when you do that the database will still set the updated_at column because of on update current_timestamp.

The way to get around this would be to use the UpdateColumns method which explicitly does not do time tracking:

err := db.Model(&user).
    Omit("user_id").
    UpdateColumns(User{
        LastLogin: lastLogin,
        UpdatedAt: found.UpdatedAt,
    }).
    Error
// error check

Overall I would consider dropping that on update clause since it's interfering with/duplicating what Gorm is doing.

Upvotes: 4

Related Questions