DEV Community

Daniel Lin
Daniel Lin

Posted on

3 3

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.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay