Pelajari cara menggunakan GORM untuk membuat record di berbagai database dalam Golang. Dapatkan panduan lengkap dengan contoh kode, unit test, dan teknik optimal untuk Simple Create, Batch Insert, Create Hooks, dan banyak lagi.
GORM adalah library ORM (Object Relational Mapper) yang populer dalam ekosistem Golang. Dengan GORM, pengembang dapat berinteraksi dengan berbagai database seperti MySQL, PostgreSQL, SQLite, dan SQL Server secara lebih mudah dan efisien.
Artikel ini akan membahas berbagai metode untuk membuat record di GORM dengan langkah-langkah yang jelas, contoh kode, serta unit test untuk memastikan fungsionalitasnya.
1. Simple Create Record
Untuk membuat record sederhana di GORM, kita dapat menggunakan metode Create().
Contoh Kode
1package main
2
3import (
4 "gorm.io/driver/sqlite"
5 "gorm.io/gorm"
6 "testing"
7)
8
9// User merepresentasikan model user dalam database
10type User struct {
11 ID uint `gorm:"primaryKey"`
12 Name string
13 Email string
14}
15
16func CreateUser(db *gorm.DB, name, email string) error {
17 user := User{Name: name, Email: email}
18 return db.Create(&user).Error
19}Penjelasan Kode:
- Pendefinisian Struct
User: Struct ini mewakili tabelusersdalam database dengan ID sebagai primary key. - Fungsi
CreateUser: Fungsi ini menerima parameter database, nama, dan email, lalu menyimpan data ke dalam database menggunakanCreate().
Unit Test dengan SQLMock
1import (
2 "database/sql"
3 "testing"
4 "github.com/DATA-DOG/go-sqlmock"
5 "gorm.io/driver/postgres"
6 "gorm.io/gorm"
7)
8
9func TestCreateUser(t *testing.T) {
10 mockDB, mock, _ := sqlmock.New()
11 sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})
12
13 mock.ExpectBegin()
14 mock.ExpectExec("INSERT INTO \"users\" \(").WillReturnResult(sqlmock.NewResult(1, 1))
15 mock.ExpectCommit()
16
17 err := CreateUser(sqlDB, "Alice", "alice@example.com")
18 if err != nil {
19 t.Errorf("Failed to create user: %v", err)
20 }
21}Penjelasan Unit Test:
- Menggunakan SQLMock untuk membuat mock database.
- Menyiapkan ekspektasi query INSERT menggunakan SQLMock.
- Memanggil
CreateUserdan memverifikasi hasilnya. - Memastikan tidak ada error selama proses penyimpanan data.
2. Create Record With Selected Fields
1func CreateUserWithSelectedFields(db *gorm.DB, name string) error {
2 return db.Select("Name").Create(&User{Name: name}).Error
3}Penjelasan Kode:
- Fungsi
CreateUserWithSelectedFields: Hanya menyimpan fieldNameke dalam database, sementara field lain sepertiEmailakan dibiarkan kosong. - Metode
Select("Name"): Menginstruksikan GORM untuk hanya menyertakan kolomNamedalam pernyataanINSERT.
Unit Test dengan SQLMock
1import (
2 "testing"
3 "github.com/DATA-DOG/go-sqlmock"
4 "gorm.io/driver/postgres"
5 "gorm.io/gorm"
6)
7
8func TestCreateSelectedFields(t *testing.T) {
9 mockDB, mock, _ := sqlmock.New()
10 sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})
11
12 mock.ExpectBegin()
13 mock.ExpectExec("INSERT INTO \"users\" \(").WillReturnResult(sqlmock.NewResult(1, 1))
14 mock.ExpectCommit()
15
16 err := CreateUserWithSelectedFields(sqlDB, "Bob")
17 if err != nil {
18 t.Errorf("Failed to create user: %v", err)
19 }
20}Penjelasan Unit Test:
- Menggunakan SQLMock untuk mensimulasikan interaksi dengan database tanpa koneksi nyata.
mock.ExpectExec()memastikan hanya kolomNameyang disertakan dalam perintahINSERT.mock.ExpectCommit()menandakan akhir transaksi database.
3. Batch Insert
1func BatchInsertUsers(db *gorm.DB, users []User) error {
2 return db.Create(&users).Error
3}Penjelasan Kode:
- Fungsi
BatchInsertUsers: Menerima slice dariUserdan menyisipkan semua record dalam satu operasi database. - Metode
Create(&users): Secara otomatis menghasilkan perintahINSERTuntuk setiap entri dalam sliceusers.
Unit Test dengan SQLMock
1func TestBatchInsert(t *testing.T) {
2 mockDB, mock, _ := sqlmock.New()
3 sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})
4
5 mock.ExpectBegin()
6 mock.ExpectExec("INSERT INTO \"users\" \(").WillReturnResult(sqlmock.NewResult(1, 2))
7 mock.ExpectCommit()
8
9 users := []User{{Name: "Charlie"}, {Name: "David"}}
10 err := BatchInsertUsers(sqlDB, users)
11 if err != nil {
12 t.Errorf("Failed to batch insert users: %v", err)
13 }
14}Penjelasan Unit Test:
- Menggunakan SQLMock untuk memverifikasi batch insert tanpa benar-benar terhubung ke database.
mock.ExpectExec()memastikan perintahINSERTmenangani lebih dari satu record.mock.ExpectCommit()menandakan transaksi selesai dan berhasil disimpan ke database.
4. Create Hooks
1func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
2 // Menambahkan prefix "Prefix-" ke field Name sebelum disimpan ke database
3 u.Name = "Prefix-" + u.Name
4 return nil
5}Unit Test dengan SQLMock
1import (
2 "testing"
3 "github.com/DATA-DOG/go-sqlmock"
4 "gorm.io/driver/postgres"
5 "gorm.io/gorm"
6)
7
8func TestCreateHooks(t *testing.T) {
9 mockDB, mock, _ := sqlmock.New()
10 sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})
11
12 // Mensimulasikan operasi database
13 mock.ExpectBegin()
14 mock.ExpectExec("INSERT INTO \"users\" ").WillReturnResult(sqlmock.NewResult(1, 1))
15 mock.ExpectCommit()
16
17 user := User{Name: "Eve"}
18 err := sqlDB.Create(&user).Error
19 if err != nil {
20 t.Errorf("Failed to create user with hook: %v", err)
21 }
22}Penjelasan Kode:
- Fungsi
BeforeCreatemenambahkan prefix “Prefix-” pada fieldNamesebelum data disimpan ke database. - Menggunakan SQLMock untuk simulasi database, sehingga tidak perlu koneksi database sungguhan.
- Unit test memverifikasi bahwa hook dijalankan dengan benar, dengan memastikan data tersimpan dengan perubahan yang diharapkan.
5. Create From Map
1func CreateUserFromMap(db *gorm.DB, data map[string]interface{}) error {
2 // Menggunakan map untuk menyimpan data ke tabel users
3 return db.Model(&User{}).Create(data).Error
4}Unit Test dengan SQLMock
1func TestCreateFromMap(t *testing.T) {
2 mockDB, mock, _ := sqlmock.New()
3 sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})
4
5 // Mensimulasikan operasi database
6 mock.ExpectBegin()
7 mock.ExpectExec("INSERT INTO \"users\" ").WillReturnResult(sqlmock.NewResult(1, 1))
8 mock.ExpectCommit()
9
10 data := map[string]interface{}{ "Name": "Charlie", "Email": "charlie@example.com" }
11 err := CreateUserFromMap(sqlDB, data)
12 if err != nil {
13 t.Errorf("Failed to create user from map: %v", err)
14 }
15}Penjelasan Kode:
- Fungsi
CreateUserFromMapmenerima data dalam bentuk map yang memungkinkan fleksibilitas dalam penyimpanan data. - Menggunakan
db.Model(&User{}).Create(data)untuk menyimpan data tanpa perlu membuat objek struct User secara eksplisit. - Unit test dengan SQLMock mensimulasikan penyimpanan data menggunakan
ExpectExecuntuk memastikan queryINSERTdieksekusi tanpa error.
6. Create From SQL Expression
1func CreateUserWithExpression(db *gorm.DB) error {
2 // Menggunakan ekspresi SQL untuk menyimpan data ke dalam tabel users
3 return db.Exec("INSERT INTO users (name, email) VALUES (?, ?)", "Generated", "generated@example.com").Error
4}Unit Test dengan SQLMock
1func TestCreateUserWithExpression(t *testing.T) {
2 mockDB, mock, _ := sqlmock.New()
3 sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})
4
5 // Mensimulasikan operasi database
6 mock.ExpectBegin()
7 mock.ExpectExec("INSERT INTO users ").WillReturnResult(sqlmock.NewResult(1, 1))
8 mock.ExpectCommit()
9
10 err := CreateUserWithExpression(sqlDB)
11 if err != nil {
12 t.Errorf("Failed to create user with SQL expression: %v", err)
13 }
14}Penjelasan Kode:
- Menggunakan
Execuntuk menjalankan SQL mentah yang memberikan fleksibilitas dalam eksekusi query. - Query
INSERT INTO users (name, email) VALUES (?, ?)digunakan untuk menyisipkan data langsung tanpa perlu menggunakan model GORM. - Unit test dengan SQLMock memverifikasi eksekusi query menggunakan
ExpectExec, memastikan bahwa query dieksekusi dengan benar. - Simulasi transaksi database dilakukan dengan
ExpectBegindanExpectCommit, memastikan perubahan dilakukan dalam transaksi yang benar.
7. Create with Associations
Saat bekerja dengan relasi antar tabel, kita bisa menyimpan data dengan asosiasi menggunakan GORM.
Contoh Kode
1type Profile struct {
2 ID uint
3 UserID uint
4 Bio string
5}
6
7type UserWithProfile struct {
8 ID uint
9 Name string
10 Profile Profile
11}
12
13func CreateUserWithProfile(db *gorm.DB, name, bio string) error {
14 user := UserWithProfile{Name: name, Profile: Profile{Bio: bio}}
15 return db.Create(&user).Error
16}Unit Test dengan SQLMock
1import (
2 "testing"
3 "github.com/DATA-DOG/go-sqlmock"
4 "gorm.io/driver/postgres"
5 "gorm.io/gorm"
6)
7
8func TestCreateUserWithProfile(t *testing.T) {
9 mockDB, mock, _ := sqlmock.New()
10 sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})
11
12 // Mensimulasikan operasi database
13 mock.ExpectBegin()
14 mock.ExpectExec("INSERT INTO \"user_with_profiles\" ").WillReturnResult(sqlmock.NewResult(1, 1))
15 mock.ExpectExec("INSERT INTO \"profiles\" ").WillReturnResult(sqlmock.NewResult(1, 1))
16 mock.ExpectCommit()
17
18 err := CreateUserWithProfile(sqlDB, "John", "Software Engineer")
19 if err != nil {
20 t.Errorf("Failed to create user with profile: %v", err)
21 }
22}Penjelasan Kode:
- Mendefinisikan model
ProfiledanUserWithProfileuntuk merepresentasikan relasi antara pengguna dan profilnya. - Fungsi
CreateUserWithProfilemembuat user baru dengan profil dan langsung menyimpan ke database. - Unit test menggunakan SQLMock untuk mensimulasikan transaksi database, memastikan operasi
INSERTdilakukan pada kedua tabel. ExpectExecdigunakan untuk memverifikasi queryINSERTpada tabel user dan profile.
8. Upsert / On Conflict
Upsert digunakan untuk menyisipkan data atau memperbarui jika sudah ada.
Contoh Kode
1func UpsertUser(db *gorm.DB, name, email string) error {
2 user := User{Name: name, Email: email}
3 return db.Clauses(gorm.Clauses{gorm.OnConflict{UpdateAll: true}}).Create(&user).Error
4}Unit Test dengan SQLMock
1func TestUpsertUser(t *testing.T) {
2 mockDB, mock, _ := sqlmock.New()
3 sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})
4
5 // Mensimulasikan operasi database
6 mock.ExpectBegin()
7 mock.ExpectExec("INSERT INTO \"users\" ").WillReturnResult(sqlmock.NewResult(1, 1))
8 mock.ExpectCommit()
9
10 err := UpsertUser(sqlDB, "Alice", "alice@example.com")
11 if err != nil {
12 t.Errorf("Failed to upsert user: %v", err)
13 }
14}Penjelasan Kode:
- Fungsi
UpsertUsermenggunakangorm.OnConflictuntuk menangani duplikasi dan memperbarui data jika sudah ada. - Unit test menggunakan SQLMock untuk memastikan operasi upsert berjalan dengan benar.
- Menggunakan
ExpectExecuntuk memverifikasi eksekusi queryINSERTdengan strategi upsert. - Simulasi transaksi database dilakukan dengan
ExpectBegindanExpectCommit, memastikan operasi berjalan sesuai aturan transaksi.
Artikel ini memberikan panduan lengkap bagi programmer Golang untuk menggunakan GORM dalam membuat record di berbagai database. Setiap teknik yang dijelaskan memiliki contoh implementasi dan unit test agar lebih mudah dipahami dan diuji dalam proyek nyata.
Untuk tutorial lebih lanjut, kunjungi: