Pelajari teknik query lanjutan di GORM dengan Golang, termasuk From SubQuery, Group Conditions, Named Arguments, IN with multiple columns, dan lainnya, lengkap dengan contoh kode dan unit test.
Artikel ini merupakan lanjutan dari pembahasan sebelumnya tentang Advanced Query dengan GORM. Kali ini, kita akan melanjutkan dengan teknik lanjutan lainnya seperti Find To Map, FirstOrInit, FirstOrCreate, Optimizer/Index Hints, Iteration, lengkap dengan contoh kode dan unit test.
8. Find To Map
Metode Find To Map digunakan untuk mengambil hasil query dan memetakannya ke dalam struktur data berbasis map. Ini berguna ketika kita ingin bekerja dengan hasil query dalam format yang lebih fleksibel.
Fungsi
1func GetUsersAsMap(db *gorm.DB) ([]map[string]interface{}, error) {
2 var results []map[string]interface{} // Deklarasi slice untuk menyimpan hasil query dalam format map
3 result := db.Model(&User{}).Find(&results) // Mengambil semua data dari tabel User dan menyimpannya ke dalam map
4 return results, result.Error // Mengembalikan hasil query dan error jika ada
5}Penjelasan Kode
var results []map[string]interface{}→ Deklarasi slice untuk menyimpan hasil query dalam format map.db.Model(&User{}).Find(&results)→ Mengambil semua data dari tabel User dan menyimpannya dalam map.return results, result.Error→ Mengembalikan hasil query dan error jika ada.
Unit Test
1func TestGetUsersAsMap_Success(t *testing.T) {
2 results, err := GetUsersAsMap(db) // Memanggil fungsi untuk mendapatkan data user dalam format map
3 assert.Nil(t, err) // Memastikan tidak ada error dalam query
4 assert.Greater(t, len(results), 0) // Memastikan hasil query tidak kosong
5}
6
7func TestGetUsersAsMap_Error(t *testing.T) {
8 db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) // Membuka database tanpa tabel User
9 results, err := GetUsersAsMap(db)
10 assert.NotNil(t, err) // Memastikan terjadi error karena tabel tidak ditemukan
11 assert.Equal(t, 0, len(results)) // Memastikan hasil query kosong
12}Penjelasan Unit Test
- Memanggil
GetUsersAsMap(db)untuk mendapatkan data dalam format map. - Memastikan tidak ada error dengan
assert.Nil(t, err). - Memastikan hasil query tidak kosong dengan
assert.Greater(t, len(results), 0). - Menguji skenario error dengan database tanpa tabel
Useruntuk memastikan error terjadi.
9. FirstOrInit
Metode FirstOrInit digunakan untuk mencari data dalam database. Jika data tidak ditemukan, maka struct akan diinisialisasi dengan nilai yang diberikan.
Fungsi
1func FirstOrInitUser(db *gorm.DB, name string) User {
2 var user User // Deklarasi variabel user
3 db.Where("name = ?", name).FirstOrInit(&user, User{Name: name}) // Jika user tidak ditemukan, inisialisasi struct User dengan nama yang diberikan
4 return user // Mengembalikan user yang ditemukan atau diinisialisasi
5}Penjelasan Kode
var user User→ Deklarasi variabelusersebagai struct User.db.Where("name = ?", name).FirstOrInit(&user, User{Name: name})→ Mencari user dengannameyang diberikan. Jika tidak ditemukan, menginisialisasi structUserdengan nilai default.return user→ Mengembalikan user yang ditemukan atau diinisialisasi.
Unit Test
1func TestFirstOrInitUser_Success(t *testing.T) {
2 user := FirstOrInitUser(db, "Alice")
3 assert.Equal(t, "Alice", user.Name)
4}
5
6func TestFirstOrInitUser_NewUser(t *testing.T) {
7 user := FirstOrInitUser(db, "Charlie")
8 assert.Equal(t, "Charlie", user.Name)
9}Penjelasan Unit Test
- Memanggil
FirstOrInitUser(db, "Alice")untuk mencari atau menginisialisasi user. - Memastikan nama user sesuai dengan yang diharapkan menggunakan
assert.Equal(t, "Alice", user.Name). - Memastikan bahwa jika user tidak ditemukan, user baru akan diinisialisasi dengan nama yang diberikan.
10. FirstOrCreate
Metode FirstOrCreate digunakan untuk mencari data dalam database dan membuatnya jika tidak ditemukan.
Fungsi
1func FirstOrCreateUser(db *gorm.DB, name string) User {
2 var user User // Deklarasi variabel user
3 db.Where("name = ?", name).FirstOrCreate(&user, User{Name: name}) // Jika user tidak ditemukan, buat user baru dengan nama yang diberikan
4 return user // Mengembalikan user yang ditemukan atau dibuat
5}Penjelasan Kode
var user User→ Deklarasi variabelusersebagai struct User.db.Where("name = ?", name).FirstOrCreate(&user, User{Name: name})→ Mencari user berdasarkanname, jika tidak ditemukan, membuat user baru dengannameyang diberikan.return user→ Mengembalikan user yang ditemukan atau dibuat.
Unit Test
1func TestFirstOrCreateUser_Success(t *testing.T) {
2 user := FirstOrCreateUser(db, "Bob")
3 assert.Equal(t, "Bob", user.Name)
4}
5
6func TestFirstOrCreateUser_NewUser(t *testing.T) {
7 user := FirstOrCreateUser(db, "David")
8 assert.Equal(t, "David", user.Name)
9}Penjelasan Unit Test
- Memanggil
FirstOrCreateUser(db, "Bob")untuk mencari atau membuat user baru. - Memastikan bahwa jika user ditemukan, user yang ada akan dikembalikan.
- Memastikan bahwa jika user tidak ditemukan, user baru akan dibuat dengan nama yang diberikan.
11. Optimizer/Index Hints
Metode ini digunakan untuk mengoptimalkan performa query dengan memberikan petunjuk kepada database mengenai indeks yang harus digunakan.
Fungsi
1func GetUsersWithIndexHint(db *gorm.DB) ([]User, error) {
2 var users []User // Deklarasi slice untuk menampung hasil query
3 result := db.Raw("SELECT * FROM users USE INDEX (idx_name)").Scan(&users) // Menjalankan query dengan hint penggunaan indeks tertentu
4 return users, result.Error // Mengembalikan hasil query dan error jika ada
5}Penjelasan Kode
var users []User→ Deklarasi slice untuk menyimpan hasil query.db.Raw("SELECT * FROM users USE INDEX (idx_name)").Scan(&users)→ Menjalankan query dengan petunjuk penggunaan indeks tertentu.return users, result.Error→ Mengembalikan hasil query dan error jika ada.
Unit Test
1func TestGetUsersWithIndexHint_Success(t *testing.T) {
2 users, err := GetUsersWithIndexHint(db)
3 assert.Nil(t, err)
4}12. Iteration
Metode ini digunakan untuk menelusuri data dalam bentuk batch kecil untuk mengurangi penggunaan memori.
Fungsi
1func IterateUsers(db *gorm.DB) error {
2 rows, err := db.Model(&User{}).Rows() // Mengambil semua baris data user dalam bentuk iterator
3 if err != nil {
4 return err
5 }
6 defer rows.Close() // Menutup iterasi setelah selesai digunakan
7 for rows.Next() {
8 var user User // Deklarasi variabel untuk menampung hasil scan
9 db.ScanRows(rows, &user) // Memindai hasil dari iterator ke struct User
10 }
11 return nil
12}Penjelasan Kode
rows, err := db.Model(&User{}).Rows()→ Mengambil semua baris data user dalam bentuk iterator.defer rows.Close()→ Menutup iterasi setelah selesai digunakan untuk menghindari kebocoran memori.for rows.Next()→ Melakukan iterasi pada setiap baris hasil query.var user User→ Deklarasi variabel untuk menampung hasil scan.db.ScanRows(rows, &user)→ Memindai hasil dari iterator ke structUser.
Unit Test
1func TestIterateUsers_Success(t *testing.T) {
2 err := IterateUsers(db)
3 assert.Nil(t, err)
4}Lanjutkan membaca untuk pembahasan FindInBatches, Query Hooks, Pluck, Scopes, dan Count.
Untuk informasi lebih lanjut, kunjungi: