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. 😅