Pemrograman

04 Create Record di GORM untuk Berbagai Database dalam Golang

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

package main

import (
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"testing"
)

// User merepresentasikan model user dalam database
type User struct {
	ID    uint   `gorm:"primaryKey"`
	Name  string
	Email string
}

func CreateUser(db *gorm.DB, name, email string) error {
	user := User{Name: name, Email: email}
	return db.Create(&user).Error
}

Penjelasan Kode:

  1. Pendefinisian Struct User: Struct ini mewakili tabel users dalam database dengan ID sebagai primary key.
  2. Fungsi CreateUser: Fungsi ini menerima parameter database, nama, dan email, lalu menyimpan data ke dalam database menggunakan Create().

Unit Test dengan SQLMock

import (
	"database/sql"
	"testing"
	"github.com/DATA-DOG/go-sqlmock"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

func TestCreateUser(t *testing.T) {
	mockDB, mock, _ := sqlmock.New()
	sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})

	mock.ExpectBegin()
	mock.ExpectExec("INSERT INTO \"users\" \(").WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectCommit()

	err := CreateUser(sqlDB, "Alice", "alice@example.com")
	if err != nil {
		t.Errorf("Failed to create user: %v", err)
	}
}

Penjelasan Unit Test:

  1. Menggunakan SQLMock untuk membuat mock database.
  2. Menyiapkan ekspektasi query INSERT menggunakan SQLMock.
  3. Memanggil CreateUser dan memverifikasi hasilnya.
  4. Memastikan tidak ada error selama proses penyimpanan data.

2. Create Record With Selected Fields

func CreateUserWithSelectedFields(db *gorm.DB, name string) error {
	return db.Select("Name").Create(&User{Name: name}).Error
}

Penjelasan Kode:

  1. Fungsi CreateUserWithSelectedFields: Hanya menyimpan field Name ke dalam database, sementara field lain seperti Email akan dibiarkan kosong.
  2. Metode Select("Name"): Menginstruksikan GORM untuk hanya menyertakan kolom Name dalam pernyataan INSERT.

Unit Test dengan SQLMock

import (
	"testing"
	"github.com/DATA-DOG/go-sqlmock"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

func TestCreateSelectedFields(t *testing.T) {
	mockDB, mock, _ := sqlmock.New()
	sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})

	mock.ExpectBegin()
	mock.ExpectExec("INSERT INTO \"users\" \(").WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectCommit()

	err := CreateUserWithSelectedFields(sqlDB, "Bob")
	if err != nil {
		t.Errorf("Failed to create user: %v", err)
	}
}

Penjelasan Unit Test:

  1. Menggunakan SQLMock untuk mensimulasikan interaksi dengan database tanpa koneksi nyata.
  2. mock.ExpectExec() memastikan hanya kolom Name yang disertakan dalam perintah INSERT.
  3. mock.ExpectCommit() menandakan akhir transaksi database.

3. Batch Insert

func BatchInsertUsers(db *gorm.DB, users []User) error {
	return db.Create(&users).Error
}

Penjelasan Kode:

  1. Fungsi BatchInsertUsers: Menerima slice dari User dan menyisipkan semua record dalam satu operasi database.
  2. Metode Create(&users): Secara otomatis menghasilkan perintah INSERT untuk setiap entri dalam slice users.

Unit Test dengan SQLMock

func TestBatchInsert(t *testing.T) {
	mockDB, mock, _ := sqlmock.New()
	sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})

	mock.ExpectBegin()
	mock.ExpectExec("INSERT INTO \"users\" \(").WillReturnResult(sqlmock.NewResult(1, 2))
	mock.ExpectCommit()

	users := []User{{Name: "Charlie"}, {Name: "David"}}
	err := BatchInsertUsers(sqlDB, users)
	if err != nil {
		t.Errorf("Failed to batch insert users: %v", err)
	}
}

Penjelasan Unit Test:

  1. Menggunakan SQLMock untuk memverifikasi batch insert tanpa benar-benar terhubung ke database.
  2. mock.ExpectExec() memastikan perintah INSERT menangani lebih dari satu record.
  3. mock.ExpectCommit() menandakan transaksi selesai dan berhasil disimpan ke database.

4. Create Hooks

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
    // Menambahkan prefix "Prefix-" ke field Name sebelum disimpan ke database
    u.Name = "Prefix-" + u.Name
    return nil
}

Unit Test dengan SQLMock

import (
    "testing"
    "github.com/DATA-DOG/go-sqlmock"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

func TestCreateHooks(t *testing.T) {
    mockDB, mock, _ := sqlmock.New()
    sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})

    // Mensimulasikan operasi database
    mock.ExpectBegin()
    mock.ExpectExec("INSERT INTO \"users\" ").WillReturnResult(sqlmock.NewResult(1, 1))
    mock.ExpectCommit()

    user := User{Name: "Eve"}
    err := sqlDB.Create(&user).Error
    if err != nil {
        t.Errorf("Failed to create user with hook: %v", err)
    }
}

Penjelasan Kode:

  1. Fungsi BeforeCreate menambahkan prefix “Prefix-” pada field Name sebelum data disimpan ke database.
  2. Menggunakan SQLMock untuk simulasi database, sehingga tidak perlu koneksi database sungguhan.
  3. Unit test memverifikasi bahwa hook dijalankan dengan benar, dengan memastikan data tersimpan dengan perubahan yang diharapkan.

5. Create From Map

func CreateUserFromMap(db *gorm.DB, data map[string]interface{}) error {
    // Menggunakan map untuk menyimpan data ke tabel users
    return db.Model(&User{}).Create(data).Error
}

Unit Test dengan SQLMock

func TestCreateFromMap(t *testing.T) {
    mockDB, mock, _ := sqlmock.New()
    sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})

    // Mensimulasikan operasi database
    mock.ExpectBegin()
    mock.ExpectExec("INSERT INTO \"users\" ").WillReturnResult(sqlmock.NewResult(1, 1))
    mock.ExpectCommit()

    data := map[string]interface{}{ "Name": "Charlie", "Email": "charlie@example.com" }
    err := CreateUserFromMap(sqlDB, data)
    if err != nil {
        t.Errorf("Failed to create user from map: %v", err)
    }
}

Penjelasan Kode:

  1. Fungsi CreateUserFromMap menerima data dalam bentuk map yang memungkinkan fleksibilitas dalam penyimpanan data.
  2. Menggunakan db.Model(&User{}).Create(data) untuk menyimpan data tanpa perlu membuat objek struct User secara eksplisit.
  3. Unit test dengan SQLMock mensimulasikan penyimpanan data menggunakan ExpectExec untuk memastikan query INSERT dieksekusi tanpa error.

6. Create From SQL Expression

func CreateUserWithExpression(db *gorm.DB) error {
    // Menggunakan ekspresi SQL untuk menyimpan data ke dalam tabel users
    return db.Exec("INSERT INTO users (name, email) VALUES (?, ?)", "Generated", "generated@example.com").Error
}

Unit Test dengan SQLMock

func TestCreateUserWithExpression(t *testing.T) {
    mockDB, mock, _ := sqlmock.New()
    sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})

    // Mensimulasikan operasi database
    mock.ExpectBegin()
    mock.ExpectExec("INSERT INTO users ").WillReturnResult(sqlmock.NewResult(1, 1))
    mock.ExpectCommit()

    err := CreateUserWithExpression(sqlDB)
    if err != nil {
        t.Errorf("Failed to create user with SQL expression: %v", err)
    }
}

Penjelasan Kode:

  1. Menggunakan Exec untuk menjalankan SQL mentah yang memberikan fleksibilitas dalam eksekusi query.
  2. Query INSERT INTO users (name, email) VALUES (?, ?) digunakan untuk menyisipkan data langsung tanpa perlu menggunakan model GORM.
  3. Unit test dengan SQLMock memverifikasi eksekusi query menggunakan ExpectExec, memastikan bahwa query dieksekusi dengan benar.
  4. Simulasi transaksi database dilakukan dengan ExpectBegin dan ExpectCommit, 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

type Profile struct {
	ID     uint
	UserID uint
	Bio    string
}

type UserWithProfile struct {
	ID      uint
	Name    string
	Profile Profile
}

func CreateUserWithProfile(db *gorm.DB, name, bio string) error {
	user := UserWithProfile{Name: name, Profile: Profile{Bio: bio}}
	return db.Create(&user).Error
}

Unit Test dengan SQLMock

import (
	"testing"
	"github.com/DATA-DOG/go-sqlmock"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

func TestCreateUserWithProfile(t *testing.T) {
	mockDB, mock, _ := sqlmock.New()
	sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})

	// Mensimulasikan operasi database
	mock.ExpectBegin()
	mock.ExpectExec("INSERT INTO \"user_with_profiles\" ").WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectExec("INSERT INTO \"profiles\" ").WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectCommit()

	err := CreateUserWithProfile(sqlDB, "John", "Software Engineer")
	if err != nil {
		t.Errorf("Failed to create user with profile: %v", err)
	}
}

Penjelasan Kode:

  1. Mendefinisikan model Profile dan UserWithProfile untuk merepresentasikan relasi antara pengguna dan profilnya.
  2. Fungsi CreateUserWithProfile membuat user baru dengan profil dan langsung menyimpan ke database.
  3. Unit test menggunakan SQLMock untuk mensimulasikan transaksi database, memastikan operasi INSERT dilakukan pada kedua tabel.
  4. ExpectExec digunakan untuk memverifikasi query INSERT pada tabel user dan profile.

8. Upsert / On Conflict

Upsert digunakan untuk menyisipkan data atau memperbarui jika sudah ada.

Contoh Kode

func UpsertUser(db *gorm.DB, name, email string) error {
	user := User{Name: name, Email: email}
	return db.Clauses(gorm.Clauses{gorm.OnConflict{UpdateAll: true}}).Create(&user).Error
}

Unit Test dengan SQLMock

func TestUpsertUser(t *testing.T) {
	mockDB, mock, _ := sqlmock.New()
	sqlDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: mockDB}), &gorm.Config{})

	// Mensimulasikan operasi database
	mock.ExpectBegin()
	mock.ExpectExec("INSERT INTO \"users\" ").WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectCommit()

	err := UpsertUser(sqlDB, "Alice", "alice@example.com")
	if err != nil {
		t.Errorf("Failed to upsert user: %v", err)
	}
}

Penjelasan Kode:

  1. Fungsi UpsertUser menggunakan gorm.OnConflict untuk menangani duplikasi dan memperbarui data jika sudah ada.
  2. Unit test menggunakan SQLMock untuk memastikan operasi upsert berjalan dengan benar.
  3. Menggunakan ExpectExec untuk memverifikasi eksekusi query INSERT dengan strategi upsert.
  4. Simulasi transaksi database dilakukan dengan ExpectBegin dan ExpectCommit, 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:

comments powered by Disqus

Topik Terhangat

pemrograman
152
jaringan
28
tips-dan-trik
27
tutorial
20
hardware
11
linux
4
kubernetes
1
trik-and-tips
1