DEV Community

Daniel Lin
Daniel Lin

Posted on

Some GORM Tips and Notes

There are some tips and GROM's notes might prevent you debugging in front of laptop all day.

Will not update the zero value

type User struct {
  ID int
  Age int
}
// if user.Age is 6 in database originally 
user.Age = 0
db.Updates(&user)
Enter fullscreen mode Exit fullscreen mode

You will find the age is not updated.

How to solve

db.Model(&user).Select("*").Omit("Role").Update(User{Role: "admin", Age: 0})
db.Model(&user).Update(map[string]interface{}{"role": 0})
Enter fullscreen mode Exit fullscreen mode

What is under the hood?

First, Most of generic in golang rely on reflect package. Gorm use reflect.IsZreo() to check if the field should be updated. source code
Second, 0 is zero value to int type, not zero value to interface{} type, because interface{} underneath is like a pointer to struct which saves object's type and reference. nil is the zero value for interface{}.

Therefore Gorm can only use Select(fields...) or map[stirng]interface{} to explicitly declare fields to be updated.

ErrNotFound

ErrNotFound will not happened in db.Find() function if not record found

WithContext

use WithContext so if the upstream ctx timeout or be cancelled the query can be cancelled too. https://gorm.io/docs/session.html#Context

timeoutCtx, _ := context.WithTimeout(context.Background(), time.Second)  

tx := db.WithContext(timeoutCtx)
tx.First(&user) // query with context timeoutCtx  
tx.Model(&user).Update("role", "admin") // update with context timeoutCtx
Enter fullscreen mode Exit fullscreen mode

WithContext is a NewSession function type in GORM is safe to reuse in same context
https://gorm.io/docs/method_chaining.html#New-Session-Method

Method chaining

Method chaining sometime can make code more readable and more reusable. you can use pointer value to have some optional query parameters.

type Filter struct {
    IDs        []int
    Type       *OrderType
    UserID     *string
}

func (r *Repository) QueryOrders(ctx context.Context, filter Filter) (orders []repository.Order, err error) {
    db := r.db.WithContext(ctx)
    if filter.UserID != nil {
        db = db.Where("user_id = ?", filter.UserID)
    }
    if len(filter.IDs) > 0 {
        db = db.Where("id in (?)", filter.IDs)
    }
    if filter.Type != nil {
        db = db.Where("type = ?", filter.Type)
    }
    if err := db.Find(&orders).Error; err != nil {
        return nil, err
    }
}
Enter fullscreen mode Exit fullscreen mode

but be careful Where is a Chaining Method in GORM the db returned by Where() is not safe to reuse.

Discussion (0)