Tutorial ini memperkenalkan dasar-dasar mengakses basis-data relasional
dengan Go menggunakan paket database/sql dari pustaka baku.
Anda akan lebih mudah mengikuti tutorial ini bila telah terbiasa dengan perkakas Go. Bila ini adalah pengalaman pertama Anda dengan Go, silahkan lihat Memulai dengan Go untuk pengenalan singkat.
Paket
database/sql
memiliki beragam tipe dan fungsi untuk terhubung ke basis-data, mengeksekusi
transaksi, membatalkan operasi yang sedang berjalan, dan banyak lagi.
Untuk rincian tentang penggunaan paket, lihat
Mengakses basis-data.
Pada tutorial ini, kita akan membuat sebuah basis-data, dan menulis kode untuk mengakses basis-data. Data yang akan kita gunakan pada proyek contoh ini yaitu album-album musik jazz lama.
Dalam tutorial ini, kita akan melewati tahap-tahap berikut:
-
Membuat direktori untuk penyimpanan kode.
-
Menyiapkan sebuah basis-data.
-
Mengimpor driver untuk basis-data.
-
Terhubung ke basis-data.
-
Mengambil banyak baris dari basis-data.
-
Mengambil sebuah baris dari basis-data.
-
Menambahkan data.
|
Note
|
Untuk tutorial lainnya, lihat halaman Tutorial. |
Kebutuhan
-
Pemasangan sistem manajemen basis-data (database management system atau DBMS) PostgreSQL.
-
Pemasangan Go. Untuk instruksi pemasangan lihat Memasang Go.
-
Perkakas untuk menyunting kode.
-
Terminal untuk mengeksekusi perintah. Go bekerja di terminal mana pun di sistem Linux dan Mac dan di
PowerShellataucmddi Windows.
Buat direktori untuk penyimpanan kode
Untuk memulai, buatlah sebuah direktori untuk kode yang akan kita tulis.
-
Buka terminal dan pindah ke direktori pengguna Anda.
Pada Linux atau Mac:
$ cd
Pada Windows:
C:\> cd %HOMEPATH%
Selanjutnya pada tutorial ini kita akan menggunakan
$sebagai tanda perintah terminal. Perintah yang akan tertera bisa berjalan pada Windows juga. -
Dari terminal, buatlah sebuah direktori bernama
data-access.$ mkdir data-access $ cd data-access
-
Buat sebuah modul untuk menyimpan dependensi yang nanti kita tambahkan selama tutorial.
Jalankan perintah
go mod init, dengan parameter nama modul.$ go mod init example/data-access go: creating new go.mod: module example/data-access
Perintah ini membuat berkas
go.modtempat dependensi yang nanti kita tambahkan akan tersimpan. Untuk informasi lanjut, lihat Manajemen dependensi.NotePada pengembangan sebenarnya, Anda sebaiknya membuat nama modul yang sesuai dengan lingkungan kerja Anda. Lebih lanjut, lihat Manajemen dependensi.
Selanjutnya kita akan membuat sebuah basis-data.
Menyiapkan sebuah basis-data
Pada langkah ini, kita akan membuat basis-data yang akan kita gunakan selama tutorial ini. Kita akan menggunakan antar-muka perintah (command-line interface atau CLI) yang disediakan oleh DBMS untuk membuat basis-data dan tabel, dan juga untuk menambahkan data.
Kita akan membuat sebuah basis-data tentang album-album jazz lama.
Perintah-perintah dalam bagian ini meggunakan PostgreSQL psql, namun kebanyakan DBMS memiliki CLI mereka sendiri dengan fitur-fitur yang mirip.
-
Buka terminal yang baru.
-
Masuk ke DBMS, berikut contoh pada PostgreSQL.
$ psql -U postgres postgres=#
-
Pada baris perintah
postgres=#, buat lah sebuah basis-data.postgres=# create database recordings;
-
Pindah lah ke basis-data yang baru kita buat untuk membuat tabel.
postgres=# \c recordings You are now connected to database "recordings" as user "postgres". recordings=#
-
Dalam direktori
data-access, buatlah sebuah berkas bernamacreate-tables.sqlyang akan menyimpan skrip SQL untuk membuat tabel-tabel. -
Di dalam berkas tersebut, tempel kode SQL berikut, kemudian simpan berkas.
DROP TABLE IF EXISTS album; CREATE TABLE album ( id SERIAL, title VARCHAR(128) NOT NULL, artist VARCHAR(255) NOT NULL, price DECIMAL(5,2) NOT NULL, PRIMARY KEY (`id`) ); INSERT INTO album (title, artist, price) VALUES ('Blue Train', 'John Coltrane', 56.99), ('Giant Steps', 'John Coltrane', 63.99), ('Jeru', 'Gerry Mulligan', 17.99), ('Sarah Vaughan', 'Sarah Vaughan', 34.98);Dalam kode SQL ini, kita:
-
Menghapus (drop) sebuah tabel bernama
album. Mengeksekusi perintah ini terlebih dahulu membuat kita lebih mudah menjalankan ulang skrip nantinya, seandainya kita akan ulang lagi dari awal. -
Membuat sebuah tabel
albumdengan empat kolom:id,title,artist, danprice. Setiap nilai kolomiddiisi oleh DBMS secara otomatis. -
Menambahkan empat baris data ke dalam tabel
album.
-
-
Dari terminal
psql, jalankan skrip yang baru kita buat tersebut.Kita gunakan perintah
\idengan cara berikut:recordings=# \i create-tables.sql
-
Lewat terminal psql, gunakan perintah
SELECTuntuk memeriksa bahwa tabel telah terbuat dan berisi data.recordings=# select * from album; id | title | artist | price ----+---------------+----------------+------- 1 | Blue Train | John Coltrane | 56.99 2 | Giant Steps | John Coltrane | 63.99 3 | Jeru | Gerry Mulligan | 17.99 4 | Sarah Vaughan | Sarah Vaughan | 34.98 (4 rows)
Selanjutnya, kita akan menulis kode Go untuk terhubung ke basis-data dan membaca data dari dalam tabel.
Mengimpor driver untuk basis-data
Setelah kita memiliki sebuah basis-data yang berisi sebuah tabel dan data, saatnya mulai menulis kode Go.
Untuk itu kita membutuhkan sebuah driver basis-data yang akan
menerjemahkan permintaan yang kita buat lewat fungsi-fungsi dalam paket
database/sql menjadi permintaan yang dapat dipahami oleh basis-data.
-
Lewat peramban, kunjungi halaman wiki SQLDrivers untuk menentukan driver yang akan kita gunakan.
Gunakan daftar di halaman tersebut untuk menentukan driver yang akan kita gunakan. Untuk mengakses PostgreSQL dalam tutorial ini, kita akan menggunakan modul github.com/lib/pq
-
Catat nama modul dari driver — yaitu,
github.com/lib/pq. -
Buat sebuah berkas
main.gountuk menulis kode Go yang disimpan dalam direktoridata-access. -
Dalam
main.go, tempel kode berikut untuk mengimpor driver.package main import "github.com/lib/pq"
Dalam kode ini, kita:
-
Menambahkan berkas kode tersebut ke dalam paket
mainsehingga kita dapat mengeksekusi-nya nanti. -
Mengimpor driver PostgreSQL
github.com/lib/pq.
-
Setelah mengimpor driver, kita akan memulai menulis kode untuk mengakses basis-data.
Terhubung ke basis-data
Sekarang tulis kode Go yang menghubungkan Anda ke basis-data.
Kita akan menggunakan pointer ke struct sql.DB, yang merepresentasikan
akses ke basis-data.
Menulis kode
-
Dalam
main.godi bawah kodeimportyang kita tambahkan sebelumnya, tempel kode Go berikut untuk membuat koneksi ke basis-data.func main() { // Contoh DATABASE_URL = "postgres://username:password@localhost:5432/database_name" databaseUrl := os.Getenv("DATABASE_URL") var connector *pq.Connector var err error connector, err = pq.NewConnector(databaseUrl) if err != nil { log.Fatal(err) } var db *sql.DB db = sql.OpenDB(connector) defer db.Close() err = db.Ping() if err != nil { log.Fatal(err) } fmt.Println("Terhubung!") }Dalam kode tersebut, kita:
-
Menggunakan
pq.NewConnectoruntuk membuat penghubung yang menerima alamat basis-data. -
Memeriksa kegagalan dari
pq.NewConnector. Fungsi ini bisa gagal bila alamat koneksi salah. Untuk mempermudah kode, kita gunakanlog.Fataluntuk mengakhiri eksekusi dan mencetak galat ke layar. -
Memanggil
sql.OpenDBuntuk menginisialiasi variabeldbdengan mengirim nilai kembalian dariNewConnector. -
Memanggil
DB.Pinguntuk memastikan bahwa koneksi ke basis-data bekerja. Pada saat program dijalankan, pemanggilansql.OpenDBbisa jadi tidak langsung terhubung ke basis-data, bergantung kepada driver yang digunakan. -
Memeriksa galat dari
Ping, bila koneksi gagal. -
Mencetak pesan bila
Pingterhubung dengan sukses.
-
-
Di bagian atas berkas
main.go, di bawah deklarasi paket, impor paket-paket yang kita butuhkan untuk mendukung kode yang telah kita tulis.Bagian atas dari berkas seharusnya seperti berikut:
package main import ( "database/sql" "fmt" "log" "os" "github.com/lib/pq" )
-
Simpan berkas
main.go
Jalankan kode
-
Tambahkan modul driver PostgreSQL sebagai dependensi.
Gunakan perintah
go getuntuk menambahkan modul "github.com/lib/pq" sebagai dependensi ke dalam modul kita. Gunakan argumen titik yang artinya "ambil semua dependensi kode dalam direktori ini".$ go get . go: added github.com/lib/pq v1.10.9
Go mengunduh dependensi tersebut karena kita menambahkannya ke dalam deklarasi
importpada langkah sebelumnya. Untuk informasi lanjut tentang pelacakan dependensi, lihat Menambahkan sebuah dependensi. -
Dari terminal, set variabel lingkungan
DATABASE_URLuntuk digunakan pada program.Pada Linux atau Mac:
$ export DATABASE_URL=postgresql://postgres@127.0.0.1/recordings?sslmode=disable
Pada Windows:
C:\> set DATABASE_URL=postgresql://postgres@127.0.0.1/recordings?sslmode=disable
-
Dari dalam direktori yang berisi
main.go, jalankan kode dengan perintahgo rundengan argumen titik yang artinya "jalankan paket main di dalam direktori ini".$ go run . Terhubung!
Kita telah terhubung! Selanjutnya, kita akan mengambil beberapa baris dari basis-data.
Mengambil banyak baris
Pada bab ini, kita akan gunakan Go untuk mengeksekusi kueri SQL untuk mengembalikan banyak baris.
Untuk perintah SQL yang mengembalikan banyak baris, kita gunakan method
Query dari paket database/sql, kemudian mengiterasi baris-baris yang
dikembalikan.
(Kita akan belajar mengambil sebuah baris nantinya, dalam bab
Mengambil sebuah baris).
Menulis kode
-
Dalam
main.go, sebelumfunc main, tempel definisi structAlbumberikut. Kita akan menggunakan struct ini untuk menyimpan data yang dikembalikan dari kueri.type Album struct { ID int64 Title string Artist string Price float32 } -
Sebelum
func main, tempel fungsialbumsByArtistuntuk kueri ke basis-data.// albumsByArtist mengambil album-album berdasarkan nama artis. func albumsByArtist(db *sql.DB, name string) ([]Album, error) { // albums adalah slice yang menyimpan data dari hasil kueri. var albums []Album rows, err := db.Query(` SELECT id, title, artist, price FROM album WHERE artist = $1`, name) if err != nil { return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) } defer rows.Close() // Iterasi pada rows, menggunakan Scan untuk menyimpan data ke dalam // struct Album. for rows.Next() { var alb Album err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price) if err != nil { return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) } albums = append(albums, alb) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) } return albums, nil }Dalam kode tersebut, kita:
-
Mendeklarasikan sebuah slice
albumsbertipeAlbum. Slice ini akan menyimpan baris-baris data kembalian dari basis-data. Field-field pada struct berkorespondensi dengan nama dan tipe kolum pada basis-data. -
Menggunakan
DB.Queryyang mengeksekusi perintahSELECTuntuk mengueri album berdasarkan nama artis.Parameter pertama dari
Queryyaitu perintah SQL. Setelah perintah SQL, kita bisa mengirim nol atau lebih parameter dengan tipe apa pun, sebagai nilai dari parameter dalam perintah SQL. Dengan memisahkan perintah SQL dari nilai parameter (bukan dengan menggabungkannya dengan, katakan lah,fmt.Sprintf), kita membuat paketdatabase/sqlmengirim nilai terpisah dari teks SQL, menghindari resiko injeksi SQL. -
Menunda penutupan
rowssampai fungsi keluar, supaya sumber yang terpakai dapat dirilis kembali ke sistem. -
Iterasi kembalian
rows, menggunakanRows.Scanuntuk mengisi setiap nilai baris kolom ke dalam field structAlbum.Scanmenerima pointer ke variabel, tempat nilai kolom akan ditulis. Di sini kita mengirim pointer ke variabelalb, dibuat menggunakan operator&.Scanmenulis lewat pointer untuk mengisi field pada struct. -
Di dalam iterasi, periksa kesalahan saat mengonversi nilai kolom ke dalam field-field struct.
-
Di dalam iterasi, tambahkan nilai
albyang baru ke dalam slicealbum. -
Setelah iterasi, periksa galat dari semua kueri, menggunakan
rows.Err. Jika kueri gagal, satu-satunya cara memeriksa galat untuk mengetahui apakah berhasil atau tidak hanyalah di sini.
-
-
Perbarui fungsi
mainsupaya memanggilalbumsByArtist.Di akhir
func main, tambahkan kode berikut.albums, err := albumsByArtist("John Coltrane") if err != nil { log.Fatal(err) } fmt.Printf("Albums ditemukan: %v\n", albums)Dalam kode di atas, kita:
-
Memanggil fungsi
albumsByArtistyang ditambahkan sebelumnya, menyimpan nilai kembalian ke variabelalbums. -
Mencetak hasil.
-
Menjalankan kode
Dari terminal, dalam direktori yang berisi main.go, jalankan kode.
$ go run .
Terhubung!
Albums ditemukan: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Selanjutnya, kita akan mengueri satu baris data.
Kueri satu baris data
Pada bab ini, kita akan mengueri sebuah baris dalam basis-data.
Untuk perintah SQL yang mengembalikan sebuah baris, kita
dapat menggunakan QueryRow, yang lebih mudah daripada Query.
Menulis kode
-
Di bawah
albumsByArtist, tempel fungsialbumByIDberikut,// albumByID kueri album berdasarkan ID. func albumByID(db *sql.DB, id int64) (Album, error) { // Variabel yang menyimpan baris kembalian dari basis-data. var alb Album row := db.QueryRow(` SELECT id, title, artist, price FROM album WHERE id = $1", id) err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price) if err != nil { if err == sql.ErrNoRows { return alb, fmt.Errorf("albumsById %d: album tidak ditemukan", id) } return alb, fmt.Errorf("albumsById %d: %v", id, err) } return alb, nil }Dalam kode tersebut, kita:
-
Menggunakan
DB.QueryRowuntuk mengeksekusi perintahSELECTuntuk mengueri sebuah album dengan ID tertentu.Fungsi
QueryRowmengembalikansql.Row. FungsiQueryRowtidak mengembalikan sebuaherror. Namun, ia akan mengembalikan sebuaherrornanti saatRow.Scandipanggil. -
Menggunakan
Row.Scanuntuk menyalin nilai kolom ke dalam field-field pada struct. -
Memeriksa galat dari
Scan.Galat
sql.ErrNoRowsmengindikasikan bahwa kueri tidak mengembalikan baris. Biasanya, galat tersebut diganti dengan teks yang lebih berarti, seperti "album tidak ditemukan".
-
-
Perbarui
mainsupaya memanggilalbumByID.Pada akhir
func main, tambahkan kode berikut.// Tulis langsung ID 2 untuk menguji kueri. alb, err := albumByID(2) if err != nil { log.Fatal(err) } fmt.Printf("Album ditemukan: %v\n", alb)Pada kode di atas, kita:
-
Memanggil fungsi
albumByIDyang baru ditambahkan. -
Mencetak album yang dikembalikan.
-
Menjalankan kode
Dari terminal, dalam direktori yang berisikan main.go, jalankan kode.
$ go run .
Terhubung!
Albums ditemukan: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Album ditemukan: {2 Giant Steps John Coltrane 63.99}
Selanjutnya, kita akan menambahkan sebuah album ke dalam basis-data.
Menambahkan data
Pada bab ini, kita akan mengeksekusi perintah SQL INSERT untuk menambahkan
sebuah baris baru pada basis-data.
Kita telah melihat cara menggunakan perintah SQL Query dan QueryRow
untuk mengambil data.
Untuk mengeksekusi perintah SQL yang tidak mengembalikan data, kita
gunakan Exec.
Khusus pada PostgreSQL, bila kita ingin mengambil ID yang baru dari hasil
INSERT kita tetap harus menggunakan QueryRow diikuti dengan Scan.
Menulis kode
-
Di bawah
albumByID, tempel fungsiaddAlbumberikut untuk mengisi sebuah album baru ke basis-data, kemudian simpanmain.go.// addAlbum menambahkan sebuah album baru ke dalam basis-data dan // mengembalikan ID album yang baru. func addAlbum(db *sql.DB, alb Album) (int64, error) { var id int64 err := db.QueryRow(` INSERT INTO album (title, artist, price) VALUES ($1, $2, $3) RETURNING id`, alb.Title, alb.Artist, alb.Price).Scan(&id) if err != nil { return 0, fmt.Errorf("addAlbum: %v", err) } return id, nil }Dalam kode ini, kita:
-
Menggunakan
DB.QueryRowuntuk mengeksekusi perintahINSERT. -
Menerima ID dari baris yang baru diisi ke basis-data menggunakan
Row.Scan -
Memeriksa galat dari pengambilan ID
-
-
Perbarui
mainsupaya memanggil fungsiaddAlbum.Pada akhir
func main, tambah kode berikut.albID, err := addAlbum(db, Album{ Title: "The Modern Sound of Betty Carter", Artist: "Betty Carter", Price: 49.99, }) if err != nil { log.Fatal(err) } fmt.Printf("ID dari album baru: %v\n", albID)Pada kode yang baru ini, kita:
-
Memanggil
addAlbumdengan sebuah album baru, dan menerima ID dari album yang baru ke variabelalbID.
-
Jalankan kode
Dari terminal, di dalam direktori yang berisi main.go, jalankan kode.
$ go run .
Terhubung!
Albums ditemukan: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Album ditemukan: {2 Giant Steps John Coltrane 63.99}
ID dari album baru: 5
Kesimpulan
Selamat! Kita baru saja menggunakan Go untuk melakukan aksi-aksi sederhana dengan basis-data relasional.
Rekomendasi topik-topik selanjutnya:
-
Lihatlah panduan data akses, yang mengikutkan informasi lanjut tentang subjek-subjek yang kita pelajari di sini.
-
Jika Anda baru belajar Go, Anda akan menemukan praktik terbaik dijelaskan dalam Efektif Go dan Menulis kode Go.
-
Tur Go adalah pengenalan langkah demi langkah dari fundamental Go.
Kode lengkap
Bab ini berisi kode lengkap untuk aplikasi yang telah kita buat selama tutorial ini.
package main
import (
"database/sql"
"fmt"
"log"
"os"
"github.com/lib/pq"
)
type Album struct {
ID int64
Title string
Artist string
Price float32
}
// albumsByArtist mengambil album-album berdasarkan nama artis.
func albumsByArtist(db *sql.DB, name string) ([]Album, error) {
// albums adalah slice yang menyimpan data dari hasil kueri.
var albums []Album
rows, err := db.Query(`
SELECT id, title, artist, price
FROM album
WHERE artist = $1`, name)
if err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
defer rows.Close()
// Iterasi pada rows, menggunakan Scan untuk menyimpan data ke dalam
// struct Album.
for rows.Next() {
var alb Album
err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price)
if err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
albums = append(albums, alb)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
return albums, nil
}
// albumByID kueri album berdasarkan ID.
func albumByID(db *sql.DB, id int64) (Album, error) {
// Variabel yang menyimpan baris kembalian dari basis-data.
var alb Album
row := db.QueryRow(`
SELECT id, title, artist, price
FROM album WHERE id = $1", id)
err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price)
if err != nil {
if err == sql.ErrNoRows {
return alb, fmt.Errorf("albumsById %d: album tidak ditemukan", id)
}
return alb, fmt.Errorf("albumsById %d: %v", id, err)
}
return alb, nil
}
// addAlbum menambahkan sebuah album baru ke dalam basis-data dan
// mengembalikan ID album yang baru.
func addAlbum(db *sql.DB, alb Album) (int64, error) {
var id int64
err := db.QueryRow(`
INSERT INTO album (title, artist, price)
VALUES ($1, $2, $3)
RETURNING id`,
alb.Title, alb.Artist, alb.Price).Scan(&id)
if err != nil {
return 0, fmt.Errorf("addAlbum: %v", err)
}
return id, nil
}
func main() {
// Contoh DATABASE_URL = "postgres://username:password@localhost:5432/database_name"
databaseUrl := os.Getenv("DATABASE_URL")
var connector *pq.Connector
var err error
connector, err = pq.NewConnector(databaseUrl)
if err != nil {
log.Fatal(err)
}
var db *sql.DB
db = sql.OpenDB(connector)
defer db.Close()
err = db.Ping()
if err != nil {
log.Fatal(err)
}
fmt.Println("Terhubung!")
albums, err := albumsByArtist(db, "John Coltrane")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Albums ditemukan: %v\n", albums)
// Tulis langsung ID 2 untuk menguji kueri.
alb, err := albumByID(db, 2)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Album ditemukan: %v\n", alb)
albID, err := addAlbum(db, Album{
Title: "The Modern Sound of Betty Carter",
Artist: "Betty Carter",
Price: 49.99,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID dari album baru: %v\n", albID)
}