Hati-Hati dengan Chaining GORM: Update Tidak Jalan Tapi Select Aman?

By Rizky Kurniawan - April 15, 2025 ~2 mins read

ruangdeveloper.com - Halo semuanya! Beberapa hari lalu saya menemukan bug cukup menjebak di salah satu service yang saya kerjakan dengan Go dan GORM. Secara logika, semuanya terlihat benar—data yang dikirim ke repository sudah sesuai, ID valid, tidak ada error, dan bahkan struct yang di-passing sudah terisi lengkap. Tapi anehnya, data tidak ter-update sama sekali di database.

Awalnya Saya Curiga Banyak Hal

Saya sempat berpikir ada yang salah di layer service atau mungkin di payload dari client. Tapi setelah tracing cukup lama, ternyata masalahnya datang dari cara saya menginisialisasi *gorm.DB di repository.

Saya menulis constructor seperti ini:

func NewUserRepository(db *gorm.DB) *userRepository {
	return &userRepository{
		model: db.Model(&entities.User{}),
	}
}

Dan di method Update saya menggunakan:

func (r *userRepository) Update(ctx context.Context, id uuid.UUID, entity *entities.User) error {
	return r.model.WithContext(ctx).Where("id = ?", id).Updates(entity).Error
}

Semua terlihat baik-baik saja — hingga saya menjalankan beberapa proses update secara berulang untuk data yang berbeda. Anehnya, entitas tidak ter-update sama sekali, padahal ID sudah benar, dan data pun valid.

Di Sini Letak Masalahnya

Karena saya menyimpan hasil chaining Model() ke dalam field model, maka seluruh query yang dibangun setelahnya (seperti WithContext, Where, dll) tetap “terikat” dengan chaining awal itu. Dan pada kasus tertentu seperti Updates(), ini menyebabkan GORM gagal menyusun query update secara benar.

Masalah ini tidak muncul saat Select() atau Find(), karena GORM bisa mendeteksi model dari parameter yang kita berikan, misalnya:

r.model.WithContext(ctx).Find(&result)

Tapi untuk Updates(), GORM sangat bergantung pada hasil chaining Model() agar tahu entitas mana yang harus di-update.

Solusinya Sederhana Tapi Krusial

Saya refactor repository constructor agar hanya menyimpan *gorm.DB mentahnya saja:

func NewUserRepository(db *gorm.DB) *userRepository {
	return &userRepository{
		db: db,
	}
}

Dan untuk setiap method, saya chaining ulang dengan Model() yang sesuai:

func (r *userRepository) Update(ctx context.Context, id uuid.UUID, entity *entities.User) error {
	return r.db.WithContext(ctx).
		Model(&entities.User{}).
		Where("id = ?", id).
		Updates(entity).Error
}

Dengan cara ini, kita bisa memastikan bahwa context dan model selalu fresh dan tidak membawa efek samping dari chaining sebelumnya.

Pelajaran Penting

  • Hindari menyimpan hasil chaining db.Model(…) di dalam struct repository.
  • Simpan saja instance *gorm.DB asli, dan lakukan chaining baru di setiap method.
  • Jangan anggap enteng chaining GORM, karena efeknya bisa muncul diam-diam tanpa error yang jelas.

Semoga pengalaman ini bisa membantu teman-teman yang menggunakan GORM agar tidak menghabiskan waktu untuk debugging seperti saya kemarin. 😅

Bagikan:

Ingin Berdiskusi?

Yuk bergabung di Grup Telegram Ruang Developer atau mulai diskusi melalui GitHub. See You!

Dapatkan contoh source code project backend, frontend, atau fullstack untuk kamu amati, tiru, dan modifikasi sesuka hati. Klik untuk melihat detail!
comments powered by Disqus

Berlangganan Gratis

Kamu akan menerima email update dari Ruang Developer

Beri Dukungan

Beri dukungan, dapatkan full source code project web untuk bahan referensi, tiru, dan modifikasi.
Lightbox