Hướng dẫn này giới thiệu những kiến thức cơ bản về việc truy cập cơ sở dữ liệu quan hệ bằng Go và package database/sql trong thư viện chuẩn.
Bạn sẽ thu được nhiều nhất từ hướng dẫn này nếu đã quen cơ bản với Go và bộ công cụ của nó. Nếu đây là lần đầu bạn tiếp xúc với Go, hãy xem Hướng dẫn: Bắt đầu với Go để có phần giới thiệu nhanh.
Package database/sql mà bạn sẽ dùng bao gồm các kiểu dữ liệu và hàm để kết nối tới cơ sở dữ liệu, thực thi transaction (giao dịch), huỷ một thao tác đang chạy, và nhiều thứ khác. Để biết thêm chi tiết về cách dùng package này, xem Truy cập cơ sở dữ liệu.
Trong hướng dẫn này, bạn sẽ tạo một cơ sở dữ liệu, rồi viết mã nguồn để truy cập cơ sở dữ liệu đó. Dự án ví dụ của bạn sẽ là một kho dữ liệu về các đĩa nhạc jazz cổ.
Trong hướng dẫn này, bạn sẽ lần lượt đi qua các phần sau:
- Tạo một thư mục cho mã nguồn của bạn.
- Thiết lập cơ sở dữ liệu.
- Import database driver.
- Lấy database handle và kết nối.
- Truy vấn nhiều row (dòng dữ liệu).
- Truy vấn một row duy nhất.
- Thêm dữ liệu.
Ghi chú: Để xem các hướng dẫn khác, xem Hướng dẫn.
Điều kiện cần
- Đã cài đặt hệ quản trị cơ sở dữ liệu quan hệ (DBMS) MySQL.
- Đã cài đặt Go. Để biết hướng dẫn cài đặt, xem Cài đặt Go.
- Một công cụ để soạn mã nguồn. Bất kỳ trình soạn thảo văn bản nào bạn có đều dùng tốt.
- Một cửa sổ dòng lệnh. Go hoạt động tốt với mọi terminal trên Linux và Mac, và với PowerShell hoặc cmd trên Windows.
Tạo một thư mục cho mã nguồn
Để bắt đầu, hãy tạo một thư mục chứa mã nguồn mà bạn sẽ viết.
-
Mở dòng lệnh và chuyển về thư mục home của bạn.
Trên Linux hoặc Mac:
$ cdTrên Windows:
C:\> cd %HOMEPATH%Trong phần còn lại của hướng dẫn, chúng tôi sẽ hiển thị dấu $ làm dấu nhắc lệnh. Các câu lệnh chúng tôi dùng cũng sẽ chạy được trên Windows.
-
Từ dòng lệnh, tạo một thư mục cho mã nguồn của bạn tên là data-access.
$ mkdir data-access $ cd data-access -
Tạo một module để bạn có thể quản lý các phụ thuộc mà bạn sẽ thêm vào trong hướng dẫn này.
Chạy câu lệnh
go mod init, truyền cho nó đường dẫn module của mã nguồn mới.$ go mod init example/data-access go: creating new go.mod: module example/data-accessCâu lệnh này tạo ra tệp go.mod, nơi các phụ thuộc bạn thêm vào sẽ được liệt kê để theo dõi. Để biết thêm, hãy xem Quản lý phụ thuộc.
Ghi chú: Trong phát triển thực tế, bạn sẽ chỉ định một đường dẫn module phù hợp hơn với nhu cầu riêng của mình. Để biết thêm, xem Quản lý phụ thuộc.
Tiếp theo, bạn sẽ tạo một cơ sở dữ liệu.
Thiết lập cơ sở dữ liệu
Ở bước này, bạn sẽ tạo cơ sở dữ liệu mà bạn sẽ làm việc cùng. Bạn sẽ dùng chính CLI của DBMS để tạo cơ sở dữ liệu và bảng, cũng như để thêm dữ liệu.
Bạn sẽ tạo một cơ sở dữ liệu chứa dữ liệu về các bản thu jazz cổ trên đĩa than.
Mã nguồn ở đây dùng MySQL CLI, nhưng hầu hết các DBMS đều có CLI riêng với tính năng tương tự.
-
Mở một cửa sổ dòng lệnh mới.
-
Tại dòng lệnh, đăng nhập vào DBMS của bạn, như ví dụ sau đây cho MySQL.
$ mysql -u root -p Enter password: mysql> -
Tại dấu nhắc lệnh
mysql, tạo một cơ sở dữ liệu.mysql> create database recordings; -
Chuyển sang cơ sở dữ liệu bạn vừa tạo để có thể thêm bảng.
mysql> use recordings; Database changed -
Trong trình soạn thảo văn bản, ở thư mục data-access, tạo một tệp tên là create-tables.sql để chứa script SQL dùng để thêm bảng.
-
Dán đoạn mã SQL sau vào tệp, rồi lưu tệp lại.
DROP TABLE IF EXISTS album; CREATE TABLE album ( id INT AUTO_INCREMENT NOT NULL, 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);Trong đoạn mã SQL này, bạn:
-
Xoá (drop) bảng tên là
album. Việc thực thi câu lệnh này trước giúp bạn dễ dàng chạy lại script sau này nếu muốn làm lại bảng từ đầu. -
Tạo bảng
albumvới bốn cột:title,artist, vàprice. Giá trịidcủa mỗi row được DBMS tạo tự động. -
Thêm bốn row với các giá trị.
-
-
Từ dấu nhắc lệnh
mysql, chạy script bạn vừa tạo.Bạn sẽ dùng câu lệnh
sourcetheo dạng sau:mysql> source /path/to/create-tables.sql -
Tại dấu nhắc lệnh của DBMS, dùng câu lệnh
SELECTđể kiểm chứng rằng bạn đã tạo thành công bảng cùng dữ liệu.mysql> 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 in set (0.00 sec)
Tiếp theo, bạn sẽ viết một ít mã Go để kết nối, từ đó có thể truy vấn dữ liệu.
Tìm và import database driver
Giờ bạn đã có một cơ sở dữ liệu với ít dữ liệu, hãy bắt đầu với mã Go.
Hãy tìm và import một database driver — thành phần sẽ chuyển các yêu cầu bạn tạo ra thông qua các hàm trong package database/sql thành các yêu cầu mà cơ sở dữ liệu hiểu được.
-
Trong trình duyệt, truy cập trang wiki SQLDrivers để xác định driver bạn có thể dùng.
Dùng danh sách trên trang đó để chọn driver bạn sẽ dùng. Để truy cập MySQL trong hướng dẫn này, bạn sẽ dùng Go-MySQL-Driver.
-
Ghi nhớ tên package của driver – ở đây là
github.com/go-sql-driver/mysql. -
Dùng trình soạn thảo văn bản, tạo một tệp để viết mã Go và lưu tệp đó với tên main.go trong thư mục data-access mà bạn đã tạo trước đó.
-
Dán đoạn mã sau vào main.go để import package driver.
package main import "github.com/go-sql-driver/mysql"Trong đoạn mã này, bạn:
-
Đặt mã nguồn của bạn vào package
mainđể có thể thực thi độc lập. -
Import MySQL driver
github.com/go-sql-driver/mysql.
-
Sau khi đã import driver, bạn sẽ bắt đầu viết mã để truy cập cơ sở dữ liệu.
Lấy database handle và kết nối
Giờ hãy viết một ít mã Go để có quyền truy cập cơ sở dữ liệu thông qua một database handle (đối tượng đại diện cho quyền truy cập cơ sở dữ liệu).
Bạn sẽ dùng một pointer tới struct sql.DB, đại diện cho quyền truy cập tới một cơ sở dữ liệu cụ thể.
Viết mã
-
Trong main.go, bên dưới đoạn mã
importbạn vừa thêm, dán đoạn mã Go sau để tạo một database handle.var db *sql.DB func main() { // Capture connection properties. cfg := mysql.NewConfig() cfg.User = os.Getenv("DBUSER") cfg.Passwd = os.Getenv("DBPASS") cfg.Net = "tcp" cfg.Addr = "127.0.0.1:3306" cfg.DBName = "recordings" // Get a database handle. var err error db, err = sql.Open("mysql", cfg.FormatDSN()) if err != nil { log.Fatal(err) } pingErr := db.Ping() if pingErr != nil { log.Fatal(pingErr) } fmt.Println("Connected!") }Trong đoạn mã này, bạn:
-
Khai báo biến
dbkiểu*sql.DB. Đây chính là database handle của bạn.Việc đặt
dblàm biến toàn cục giúp ví dụ này đơn giản hơn. Trong môi trường production, bạn nên tránh dùng biến toàn cục, chẳng hạn bằng cách truyền biến đó vào các hàm cần dùng, hoặc bọc nó trong một struct. -
Dùng
Configcủa MySQL driver – cùng với phương thứcFormatDSNcủa kiểu đó -– để thu thập các thuộc tính kết nối và định dạng chúng thành một DSN (chuỗi mô tả nguồn dữ liệu) cho chuỗi kết nối.Struct
Configcho ra đoạn mã dễ đọc hơn so với việc viết thẳng một chuỗi kết nối. -
Gọi
sql.Openđể khởi tạo biếndb, truyền vào giá trị trả về củaFormatDSN. -
Kiểm tra lỗi từ
sql.Open. Nó có thể thất bại nếu, ví dụ, các thông số kết nối cơ sở dữ liệu của bạn không đúng định dạng.Để đơn giản hoá mã nguồn, bạn gọi
log.Fatalđể kết thúc chương trình và in lỗi ra console. Trong mã nguồn production, bạn sẽ muốn xử lý lỗi một cách mềm mại hơn. -
Gọi
DB.Pingđể xác nhận rằng việc kết nối tới cơ sở dữ liệu hoạt động. Khi chạy,sql.Opencó thể không kết nối ngay lập tức, tuỳ theo driver. Ở đây bạn dùngPingđể xác nhận rằng packagedatabase/sqlcó thể kết nối khi cần. -
Kiểm tra lỗi từ
Ping, phòng trường hợp kết nối thất bại. -
In ra một thông báo nếu
Pingkết nối thành công.
-
-
Gần đầu tệp main.go, ngay bên dưới khai báo package, hãy import các package cần thiết để hỗ trợ đoạn mã bạn vừa viết.
Phần đầu tệp giờ sẽ trông như sau:
package main import ( "database/sql" "fmt" "log" "os" "github.com/go-sql-driver/mysql" ) -
Lưu main.go.
Chạy mã
-
Bắt đầu theo dõi module MySQL driver như một phụ thuộc.
Dùng
go getđể thêm module github.com/go-sql-driver/mysql làm phụ thuộc cho module của bạn. Dùng đối số dấu chấm với ý nghĩa “lấy các phụ thuộc cho mã nguồn trong thư mục hiện tại.”$ go get . go: added filippo.io/edwards25519 v1.1.0 go: added github.com/go-sql-driver/mysql v1.8.1Go đã tải phụ thuộc này về vì bạn đã thêm nó vào khai báo
importở bước trước. Để biết thêm về việc theo dõi phụ thuộc, xem Thêm một phụ thuộc. -
Từ dòng lệnh, đặt các biến môi trường
DBUSERvàDBPASSđể chương trình Go sử dụng.Trên Linux hoặc Mac:
$ export DBUSER=username $ export DBPASS=passwordTrên Windows:
C:\Users\you\data-access> set DBUSER=username C:\Users\you\data-access> set DBPASS=password -
Từ dòng lệnh trong thư mục chứa main.go, chạy mã nguồn bằng cách gõ
go runvới đối số dấu chấm, mang ý nghĩa “chạy package trong thư mục hiện tại.”$ go run . Connected!
Bạn đã kết nối được! Tiếp theo, bạn sẽ truy vấn một ít dữ liệu.
Truy vấn nhiều row
Ở phần này, bạn sẽ dùng Go để thực thi một truy vấn SQL được thiết kế để trả về nhiều row.
Với các câu lệnh SQL có thể trả về nhiều row, bạn dùng phương thức Query của package database/sql, rồi lặp qua các row mà nó trả về. (Bạn sẽ học cách truy vấn một row duy nhất ở phần sau, trong mục Truy vấn một row duy nhất.)
Viết mã
-
Trong main.go, ngay phía trên
func main, dán định nghĩa sau của structAlbum. Bạn sẽ dùng nó để chứa dữ liệu của row trả về từ truy vấn.type Album struct { ID int64 Title string Artist string Price float32 } -
Bên dưới
func main, dán hàmalbumsByArtistsau đây để truy vấn cơ sở dữ liệu.// albumsByArtist queries for albums that have the specified artist name. func albumsByArtist(name string) ([]Album, error) { // An albums slice to hold data from returned rows. var albums []Album rows, err := db.Query("SELECT * FROM album WHERE artist = ?", name) if err != nil { return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) } defer rows.Close() // Loop through rows, using Scan to assign column data to struct fields. for rows.Next() { var alb Album if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); 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 }Trong đoạn mã này, bạn:
-
Khai báo một slice
albumskiểuAlbummà bạn đã định nghĩa. Nó sẽ chứa dữ liệu từ các row trả về. Tên và kiểu của các trường trong struct tương ứng với tên và kiểu của các cột trong cơ sở dữ liệu. -
Dùng
DB.Queryđể thực thi câu lệnhSELECTnhằm truy vấn các album có tên nghệ sĩ được chỉ định.Tham số đầu tiên của
Querylà câu lệnh SQL. Sau tham số đó, bạn có thể truyền không hoặc nhiều tham số thuộc kiểu bất kỳ. Chúng cho bạn chỗ để chỉ định giá trị cho các tham số trong câu lệnh SQL. Bằng cách tách câu lệnh SQL khỏi giá trị của tham số (thay vì nối chuỗi chúng lại, chẳng hạn bằngfmt.Sprintf), bạn cho phép packagedatabase/sqlgửi các giá trị tách rời khỏi phần văn bản SQL, loại bỏ mọi rủi ro SQL injection. -
Defer việc đóng
rowsđể mọi tài nguyên mà nó giữ sẽ được giải phóng khi hàm kết thúc. -
Lặp qua các row trả về, dùng
Rows.Scanđể gán giá trị các cột của mỗi row vào các trường của structAlbum.Scannhận một danh sách pointer tới các giá trị Go, nơi giá trị của các cột sẽ được ghi vào. Ở đây, bạn truyền các pointer tới những trường trong biếnalb, được tạo bằng toán tử&.Scanghi thông qua các pointer đó để cập nhật các trường của struct. -
Bên trong vòng lặp, kiểm tra lỗi khi scan giá trị các cột vào các trường của struct.
-
Bên trong vòng lặp, thêm
albmới vào slicealbums. -
Sau vòng lặp, kiểm tra lỗi của toàn bộ truy vấn bằng
rows.Err. Lưu ý rằng nếu bản thân truy vấn thất bại, kiểm tra lỗi ở đây là cách duy nhất để biết rằng kết quả chưa đầy đủ.
-
-
Cập nhật hàm
mainđể gọialbumsByArtist.Ở cuối
func main, thêm đoạn mã sau.albums, err := albumsByArtist("John Coltrane") if err != nil { log.Fatal(err) } fmt.Printf("Albums found: %v\n", albums)Trong đoạn mã mới, giờ bạn:
-
Gọi hàm
albumsByArtistmà bạn đã thêm, gán giá trị trả về của nó cho biến mớialbums. -
In kết quả ra.
-
Chạy mã
Từ dòng lệnh trong thư mục chứa main.go, chạy mã nguồn.
$ go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Tiếp theo, bạn sẽ truy vấn một row duy nhất.
Truy vấn một row duy nhất
Ở phần này, bạn sẽ dùng Go để truy vấn một row duy nhất trong cơ sở dữ liệu.
Với các câu lệnh SQL mà bạn biết chắc sẽ trả về nhiều nhất một row, bạn có thể dùng QueryRow, đơn giản hơn so với việc dùng vòng lặp với Query.
Viết mã
-
Bên dưới
albumsByArtist, dán hàmalbumByIDsau đây.// albumByID queries for the album with the specified ID. func albumByID(id int64) (Album, error) { // An album to hold data from the returned row. var alb Album row := db.QueryRow("SELECT * FROM album WHERE id = ?", id) if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil { if err == sql.ErrNoRows { return alb, fmt.Errorf("albumsById %d: no such album", id) } return alb, fmt.Errorf("albumsById %d: %v", id, err) } return alb, nil }Trong đoạn mã này, bạn:
-
Dùng
DB.QueryRowđể thực thi câu lệnhSELECTnhằm truy vấn album có ID được chỉ định.Nó trả về một
sql.Row. Để đơn giản hoá mã nguồn gọi nó (chính là mã của bạn!),QueryRowkhông trả về lỗi. Thay vào đó, nó thu xếp để trả về mọi lỗi truy vấn (chẳng hạnsql.ErrNoRows) từRows.Scansau đó. -
Dùng
Row.Scanđể sao chép giá trị các cột vào các trường của struct. -
Kiểm tra lỗi từ
Scan.Lỗi đặc biệt
sql.ErrNoRowscho biết truy vấn không trả về row nào. Thông thường, lỗi đó đáng được thay bằng một thông báo cụ thể hơn, chẳng hạn “no such album” như ở đây.
-
-
Cập nhật
mainđể gọialbumByID.Ở cuối
func main, thêm đoạn mã sau.// Hard-code ID 2 here to test the query. alb, err := albumByID(2) if err != nil { log.Fatal(err) } fmt.Printf("Album found: %v\n", alb)Trong đoạn mã mới, giờ bạn:
-
Gọi hàm
albumByIDmà bạn đã thêm. -
In ra ID của album trả về.
-
Chạy mã
Từ dòng lệnh trong thư mục chứa main.go, chạy mã nguồn.
$ go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Album found: {2 Giant Steps John Coltrane 63.99}
Tiếp theo, bạn sẽ thêm một album vào cơ sở dữ liệu.
Thêm dữ liệu
Ở phần này, bạn sẽ dùng Go để thực thi câu lệnh SQL INSERT nhằm thêm một row mới vào cơ sở dữ liệu.
Bạn đã thấy cách dùng Query và QueryRow với các câu lệnh SQL có trả về dữ liệu. Để thực thi các câu lệnh SQL không trả về dữ liệu, bạn dùng Exec.
Viết mã
-
Bên dưới
albumByID, dán hàmaddAlbumsau đây để chèn một album mới vào cơ sở dữ liệu, rồi lưu tệp main.go.// addAlbum adds the specified album to the database, // returning the album ID of the new entry func addAlbum(alb Album) (int64, error) { result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", alb.Title, alb.Artist, alb.Price) if err != nil { return 0, fmt.Errorf("addAlbum: %v", err) } id, err := result.LastInsertId() if err != nil { return 0, fmt.Errorf("addAlbum: %v", err) } return id, nil }Trong đoạn mã này, bạn:
-
Dùng
DB.Execđể thực thi câu lệnhINSERT.Giống như
Query,Execnhận một câu lệnh SQL, theo sau là các giá trị tham số cho câu lệnh SQL đó. -
Kiểm tra lỗi từ thao tác
INSERT. -
Lấy ID của row vừa được chèn vào cơ sở dữ liệu bằng
Result.LastInsertId. -
Kiểm tra lỗi từ thao tác lấy ID.
-
-
Cập nhật
mainđể gọi hàmaddAlbummới.Ở cuối
func main, thêm đoạn mã sau.albID, err := addAlbum(Album{ Title: "The Modern Sound of Betty Carter", Artist: "Betty Carter", Price: 49.99, }) if err != nil { log.Fatal(err) } fmt.Printf("ID of added album: %v\n", albID)Trong đoạn mã mới, giờ bạn:
- Gọi
addAlbumvới một album mới, gán ID của album bạn đang thêm vào biếnalbID.
- Gọi
Chạy mã
Từ dòng lệnh trong thư mục chứa main.go, chạy mã nguồn.
$ go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Album found: {2 Giant Steps John Coltrane 63.99}
ID of added album: 5
Kết luận
Chúc mừng! Bạn vừa dùng Go để thực hiện các thao tác đơn giản với một cơ sở dữ liệu quan hệ.
Các chủ đề tiếp theo được gợi ý:
-
Hãy xem qua hướng dẫn truy cập dữ liệu, trong đó có thêm thông tin về những chủ đề chỉ được đề cập sơ lược ở đây.
-
Nếu bạn mới làm quen với Go, bạn sẽ tìm thấy những thực hành tốt hữu ích được mô tả trong Effective Go và Cách viết mã Go.
-
Go Tour là phần giới thiệu từng bước rất hay về các kiến thức nền tảng của Go.
Mã nguồn hoàn chỉnh
Phần này chứa mã nguồn của ứng dụng mà bạn xây dựng trong hướng dẫn này.
package main
import (
"database/sql"
"fmt"
"log"
"os"
"github.com/go-sql-driver/mysql"
)
var db *sql.DB
type Album struct {
ID int64
Title string
Artist string
Price float32
}
func main() {
// Capture connection properties.
cfg := mysql.NewConfig()
cfg.User = os.Getenv("DBUSER")
cfg.Passwd = os.Getenv("DBPASS")
cfg.Net = "tcp"
cfg.Addr = "127.0.0.1:3306"
cfg.DBName = "recordings"
// Get a database handle.
var err error
db, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
log.Fatal(err)
}
pingErr := db.Ping()
if pingErr != nil {
log.Fatal(pingErr)
}
fmt.Println("Connected!")
albums, err := albumsByArtist("John Coltrane")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Albums found: %v\n", albums)
// Hard-code ID 2 here to test the query.
alb, err := albumByID(2)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Album found: %v\n", alb)
albID, err := addAlbum(Album{
Title: "The Modern Sound of Betty Carter",
Artist: "Betty Carter",
Price: 49.99,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID of added album: %v\n", albID)
}
// albumsByArtist queries for albums that have the specified artist name.
func albumsByArtist(name string) ([]Album, error) {
// An albums slice to hold data from returned rows.
var albums []Album
rows, err := db.Query("SELECT * FROM album WHERE artist = ?", name)
if err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
defer rows.Close()
// Loop through rows, using Scan to assign column data to struct fields.
for rows.Next() {
var alb Album
if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); 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 queries for the album with the specified ID.
func albumByID(id int64) (Album, error) {
// An album to hold data from the returned row.
var alb Album
row := db.QueryRow("SELECT * FROM album WHERE id = ?", id)
if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
if err == sql.ErrNoRows {
return alb, fmt.Errorf("albumsById %d: no such album", id)
}
return alb, fmt.Errorf("albumsById %d: %v", id, err)
}
return alb, nil
}
// addAlbum adds the specified album to the database,
// returning the album ID of the new entry
func addAlbum(alb Album) (int64, error) {
result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", alb.Title, alb.Artist, alb.Price)
if err != nil {
return 0, fmt.Errorf("addAlbum: %v", err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("addAlbum: %v", err)
}
return id, nil
}