Your Question

Some code to handle updates that worked in GORM 1 has stopped working since the update, and I'm not sure why. It appears using Save only applies updates to the parent resource, and none of its associations.

Consider the following model:

type Car struct {
    ID        int
    Kind      string
    Value     float32
    PersonID  int
}
type Person struct {
    ID      int
    Name    string
    Car     Car     `gorm:"foreignkey:PersonID;"`
}

The following code creates an instance of Person, then modifies a few of its fields, and then performs a Save on the updated struct. However, after retrieving the updated resource, only the parent has been properly updated. The child remains unchanged

person := &Person{Name: "Jinzhu", Car: Car{Kind: "Audi", Value: 10000}}
db.Create(person)

id := person.ID

person.Car.Value = 9000
person.Name = "Jinzhu 2"
db.Save(person)

updatedPerson := &Person{}
db.Preload(clause.Associations).Take(updatedPerson, "id = ?", id)

fmt.Println(updatedPerson.Name == person.Name)              // TRUE
fmt.Println(updatedPerson.Car.Value == person.Car.Value)    // FALSE

The SQL Generated upon calling Save is as follows:

INSERT INTO "cars" ("kind","value","person_id","id") VALUES ('Audi',9000.000000,1,1) ON CONFLICT ("id") DO UPDATE SET "person_id"="excluded"."person_id" RETURNING "id";
UPDATE "people" SET "name"='Jinzhu 2' WHERE "id" = 1;

When I execute the first SQL statement directly in my PostgreSQL database, no changes get applied to the cars row.

The document you expected this should be explained

Update

Expected answer

Is there something I need to do differently to get the update on the child to work, or has something changed in GORM 2?

Comment From: ocomsoft

I had the same issue - I posted a question on StackOverflow here https://stackoverflow.com/questions/63933736/gorm-association-not-updating hope this helps.

Comment From: jinzhu

Hi @ocomsoft @Robbie-Perry

GORM V2 switched to use upsert to save associations, so save changed fields won't works in V2. (to avoid save uncompleted associations to protect your data)

https://gorm.io/docs/associations.html#Auto-Create-Update

Gorm How to properly apply updates to associations

But forgot to mention that in the breaking changes log, will update it.

Thank you.

Comment From: Robbie-Perry

@jinzhu Ok, thanks for the reply. Is there any way of working around this?

Comment From: ocomsoft

Could there be an option to change it so they are updated? Otherwise we have to update them twice

Comment From: jinzhu

Would suggest you to write code like:

db.Model(&person.Car).Update("Value", 9000)
db.Model(&person).Updates(Person{Name: "Jinzhu 2"})

db.Model(&person).Updates(map[string]interface{}{"Name": "Jinzhu 2"})

This will have less code and better performance, and GORM will update person.Car.Value and person.Name's value to the correct one.

Comment From: ocomsoft

@jinzhu Thanks for your reply. But that doesn't work for me because my use case is a REST API Where I need to ensure the entire JSON tree is updated. In my case I am also talking about Many to Many so I would need to save the root object then also save each association object for each association to ensure all the data is updated in the database. Therefore doing more database calls. My example (https://stackoverflow.com/questions/63933736/gorm-association-not-updating) was just to show the problem it's not how I am using GORM.

The way I see it at the moment it only Inserts New associated 'objects' and doesn't update them. Maybe the documentation needs to change to make this clear. I expected it to save all the objects.

Comment From: rifqimf12

Thanks for the suggestion @jinzhu, i had the issue where the join table from many2many associations aren't updated. It seem we can't do:

db.Model(&person.Friends).Update(newFriendsSlice)

Is there any workaround for this case?

Comment From: Robbie-Perry

@jinzhu Thanks for the reply, but I'm in a similar situation to @ocomsoft , except I have to perform updates on an arbitrary data type, and have no way of knowing what the relations are. As a result, I'm not sure if GORM is any longer viable for my usecase.

Comment From: jinzhu

Hi @Robbie-Perry @rifqi12 @ocomsoft

DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user)

Will save all fields of associations, thank you for your report.

Comment From: Robbie-Perry

@jinzhu Thank you very much for this! You just earned yourself a donation

Comment From: IamFaizanKhalid

Hi @Robbie-Perry @rifqi12 @ocomsoft

go DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user)

Will save all fields of associations, thank you for your report.

You are a life saver @jinzhu .. ❤️

Comment From: LukeLaScala

Hi @Robbie-Perry @Rifqi12 @ocomsoft

go DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user)

Will save all fields of associations, thank you for your report.

This is creating a new row, and not updating an existing row. If it makes any difference I have changed baseModel to use UUID instead of ID.

Comment From: sngyai

Hi @Robbie-Perry @Rifqi12 @ocomsoft go DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user)

Will save all fields of associations, thank you for your report.

This is creating a new row, and not updating an existing row. If it makes any difference I have changed baseModel to use UUID instead of ID.

Same issue, I also want to update an existed row by ID, but instead it create a new.

Comment From: cesc1802

I have the same issue. anyone who have resolved it can help me. thanks

Comment From: LukeLaScala

Make sure you mark the field as unique if you'd like it to create a new one!

Comment From: rikikun

Hi @Robbie-Perry @Rifqi12 @ocomsoft go DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user)

Will save all fields of associations, thank you for your report.

This is creating a new row, and not updating an existing row. If it makes any difference I have changed baseModel to use UUID instead of ID.

I also found this behaviour but in my problem when I Save my model like

DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&person)

log show that gorm use SQL" UPDATE" for person table but use SQL "INSERT" for car

the real problem is I forgot to insert ID for car

Before. UPDATE(person), INSERT(car)

person := &Person{
                 ID: 2,
        Name: "test",
                Car: {
                    Kind: "test kind",
                    Value: 1
                }
    }

After. UPDATE(person), UPDATE(car)

person := &Person{
                 ID: 2,                 
        Name: "test",
                Car: {
                        ID: 1 // same ID that generate from create action I need to get and assign it first
                    Kind: "test kind",
                    Value: 1
                }
    }

I hope this helpful.

Comment From: maczg

I was wondering if it's also possible remove missing associations on updating "parent" struct.

Example

    c1 := Child{
    Model: gorm.Model{ID:1 },
    Name:  "Child1"}

    d := &Dad{
        Model:    gorm.Model{ID: 1},
        Name:     "Dad1",
        Children: []*Child{&c1},
    }

       DB.Create(&d)

    //set childs empty 
     d.Children = append(
        d.Children[:0],
        d.Children[1:]...,
    )

  //remove existing Child 
  DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&d)

Comment From: sanjaykrishnanem

Hi @Robbie-Perry @Rifqi12 @ocomsoft go DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user)

Will save all fields of associations, thank you for your report.

This is creating a new row, and not updating an existing row. If it makes any difference I have changed baseModel to use UUID instead of ID.

I also found this behaviour but in my problem when I Save my model like

DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&person)

log show that gorm use SQL" UPDATE" for person table but use SQL "INSERT" for car

the real problem is I forgot to insert ID for car

Before. UPDATE(person), INSERT(car)

person := &Person{ ID: 2, Name: "test", Car: { Kind: "test kind", Value: 1 } }

After. UPDATE(person), UPDATE(car)

person := &Person{ ID: 2, Name: "test", Car: { ID: 1 // same ID that generate from create action I need to get and assign it first Kind: "test kind", Value: 1 } }

I hope this helpful.

Same here, when I tried saving a model after updating it, on save the associated models are getting inserted again and again instead of being updated. @jinzhu can you please help out with this, thanks!

Comment From: okcthulhu

I'm still seeing this issue as well when attempting to upsert associations with version 1.23.5. I've verified the IDs are set, which is why I receive a foreign key uniqueness constraint error as it is attempting to insert the same record rather than update it.

Screen Shot 2022-06-29 at 4 24 36 PM

type StayntouchReservation struct {
    Model
    ReservationSourceID uuid.UUID          `gorm:"reservation_source_id,omitempty"`
    ReservationSource   *ReservationSource `gorm:"foreignKey:ReservationSourceID"`
}
func (r *Repository) CreateOrUpdateStayntouchReservation(ctx context.Context, stayntouchReservation *StayntouchReservation) (*StayntouchReservation, error) {
    tx := r.DB.
        WithContext(ctx).
        Session(&gorm.Session{FullSaveAssociations: true}).
        Omit(
            "ReservationSource.Reservation.Property",
            "ReservationSource.Reservation.Unit",
            "ReservationSource.Reservation.Members",
        ).
        Save(&stayntouchReservation)
    if tx.Error != nil {
        log.Err(tx.Error)
        return nil, tx.Error
    }

    return stayntouchReservation, nil
}

Any ideas?

Comment From: ssmmtt

func (s *Service) UpdateInfo(cus License.Customer) (err error) {
    db := global.GVA_DB.Model(&License.Customer{})
    res := db.Session(&gorm.Session{FullSaveAssociations: true}).Model(cus).Updates(cus)
    //res := db.Model(cus).Updates(cus)
    fmt.Println(res)
    return nil
}

Comment From: jwping

MySQL insert means that the record will be updated if it exists and created if it does not exist. However, my associated substructure defines that the ID is updated in beforecreate (or beforesave), which causes a hook to be triggered when the associated table is updated. The ID is overwritten with a new ID and the insertion operation occurs. In fact, I want to update the record. This is my problem. I don't know if it is helpful to you

Comment From: hemantsr

We are also using associations and issue we are facing is created_at time is being updated of record every time Update is performed. Query being performed is INERT <> ON DUPLICATE KEY SET <>. How, can we avoid updating few fields during Update with associations?

Comment From: itsatony

@jwping : had the same issue - a simple
if entity.Id == "" { entity.Id = generateId() } in "BeforeCreate" fixed the creation of multiple db entities whenever an association is updated.

Comment From: yayapao

Troubled in the same issue, many2many can just insert, and ignore delete or update record.

Here is my solutions, it worked but not elegant.

// UpdateUser before update, remove its associations first
func (o *User) UpdateUser() error {
    if len(o.Language) != 0 {
        err := DBClient.Model(&User{ID: o.ID}).Association("Language").Clear()
        if err != nil {
            return err
        }
    }
    err := DBClient.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&o).Error
    if err != nil {
        return err
    }
    return nil
}

Comment From: KiddoV

go DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user) Anyone know how do I prevent parent table creation when associate tables failed to create?

Comment From: AutismPatient

To solve the problem of many2many's join table being added indefinitely instead of being updated as it exists, you can add a union index to the join table relationship, which will be associated with the foreign key of the referenced table. I think this is related to the ON DUPLICATE KEY UPDATE in the statement, It will work through the Unique record

Comment From: luizcarlosrodrigues

I´m still having problem with this same code because when i use the statement below, if my association are just updating one column, it updates all columns with null values:

database.Db.WithContext(ctx).Session(&gorm.Session{FullSaveAssociations:true}).Clauses(clause.Returning{}).Updates(&client)

For this example I have a table and an association that is updating just one field and gorm is updating all the other fields to null

type Persons struct {
    ClientId          *string            `gorm:"column:uuid;primaryKey;not null;default:null" json:"clientId"`
    Name              *string            `gorm:"column:name;not null;default:null" json:"name"`
    Documents         []*PersonDocuments `gorm:"foreignKey:PersonId" json:"documents,omitempty"`
}
type PersonAddress struct {
    AddressId        *string `gorm:"column:uuid;primaryKey;not null;default:null" json:"addressId"`
    PersonId         *string `gorm:"column:person_id;not null;default:null" json:"clientId"`
    CountryCode      *string `gorm:"column:country_code;not null;default:null" json:"countryCode"`
    StatusCode       *int    `gorm:"column:status_code;not null;default:1" json:"statusCode"`
}

I´m sending the person object but i just need to update the statusCode of the Address association

Comment From: pavittarsingh315

Hi, I'm having a similar problem where a nested object is being inserted rather than updated

// My custom version of gorm.Model
type Base struct {
    Id        string    `json:"id" gorm:"primary_key"
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}
type User struct {
    Base
    Username string
    Password string
    Cards []Card
}
type Card struct {
    Base
    UserId string
    Number int `gorm:"unique"`
}

The problem is, when I have a User object and try to save it:

var user *User
db.First(&user, "id = ?", <some id>)

user.Username = <some other value>
db.Save(&user)

It tries to UPDATE the user table but tries to INSERT into the card table which causes a unique constraint error on the Number column Is there a way to fix this @jinzhu.

The thing is, if I just put the Base struct fields into the respective models rather than embed it, it all works fine.

Comment From: pavittarsingh315

I sort of fixed it. I simply added a check in BeforeCreate:

func (b *Base) BeforeCreate(tx *gorm.DB) error {
    if b.Id == "" {
        b.Id = uuid.NewString()
    }
    return nil
}

Originally, I didn't have the if block. Now it works because the ORM is no longer generating a new id.

The problem is, it is still an INSERT query for card and not UPDATE. I understand that if I use Session(&gorm.Session{FullSaveAssociations: true}), its doing an update in all but name. Is this the intended behavior?