こんにちは。よっしーです(^^)
今日は、先日の記事に引き続き、GolangでDB操作をする方法についてご紹介します。
概要
本記事は前回の記事からの続きになります。前回の記事は下記になりますので、未参照の方は一読することをおすすめします。
GolangにおけるDBへの接続方法については、下記のセクションで進めています。
- コード用のフォルダを作成する。
- データベースをセットアップする。
- データベースドライバーをインポートします。
- データベースハンドルを取得し、接続します。
- 複数行のクエリを実行する。
- 1行のクエリを実行する。
- データを追加する。
前回の記事では、セクション4までをご紹介しました。本記事では、セクション5以降についてご紹介しています。
複数行のクエリを実行する。
このセクションでは、Golangを使用して複数行を返すように設計されたSQLクエリを実行します。
複数行を返す可能性のあるSQL文では、database/sqlパッケージのQueryメソッドを使用し、それが返す行をループします。(単一の行に対するクエリの方法は、後で「単一の行に対するクエリ」のセクションでご紹介します)。
main.goのfunc mainのすぐ上に、以下のアルバム構造体の定義を貼り付けます。これを使用して、クエリから返された行データを保持します。
type Album struct {
ID int64
Title string
Artist string
Price float32
}
func mainの下に、以下のalbumsByArtist関数を貼り付けて、データベースに問い合わせる。
// 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
}
メイン関数を更新して albumsByArtist を呼び出します。
func mainの最後に、以下のコードを追加する。
albums, err := albumsByArtist("John Coltrane")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Albums found: %v\n", albums)
main.goのあるディレクトリのコマンドラインから、コードを実行する。
go run .
下記のような出力なれば成功です。
% go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
もし、下記のようなエラーになった場合は、データベースが起動していないことが想定されるため、下記の手順でデータベースを起動させます。
cd ../database
docker compose up -d
export DBUSER=root
export DBPASS=P@ssw0rd
1行のクエリを実行する。
このセクションでは、Goを使ってデータベース内の1行を検索します。
最大でも1行しか返さないことがわかっているSQL文では、QueryRowを使用することができます。
albumsByArtist の下に、以下の albumByID 関数を貼り付けます。
// 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
}
albumByID を呼び出すように main を更新する。
func mainの最後に、以下のコードを追加する。
// 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)
main.goのあるディレクトリのコマンドラインから、コードを実行する。
go run .
下記のような出力なれば成功です。
% 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}
データを追加する。
このセクションでは、Goを使用してSQL INSERT文を実行し、データベースに新しい行を追加します。
データを返す SQL 文で Query と QueryRow を使用する方法を説明しました。データを返さない SQL 文を実行するには、Exec メソッドを利用します。
albumByIDの下に、以下のaddAlbum関数を貼り付けて、データベースに新しいアルバムを挿入し、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
}
新しいaddAlbum関数を呼び出すようにmainを更新する。
func mainの最後に、以下のコードを追加する。
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)
main.goのあるディレクトリのコマンドラインから、コードを実行する。
go run .
下記のような出力なれば成功です。
% 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
解説
albumsByArtist メソッド
このコードは、指定されたアーティスト名を持つアルバムを検索するための関数「albumsByArtist」を定義しています。
関数の引数は「name」という文字列で、検索するアーティストの名前を指定します。
関数内で行われる主要な処理は次のとおりです:
albums
というAlbum
型のスライスを宣言します。このスライスは、検索結果のアルバムデータを格納するために使用されます。db.Query
関数を使用して、指定されたアーティスト名に一致する行をデータベースから取得します。SQLクエリは「SELECT * FROM album WHERE artist = ?」となっており、プレースホルダー「?」にはname
が代入されます。- エラーチェックを行います。もしエラーが発生した場合は、エラーメッセージを返して関数を終了します。
rows
は、クエリの結果セットを表す行のイテレータです。rows.Close()
を使用して、結果セットのクローズ処理を遅延させます。これにより、関数の実行が終了するときにクエリ結果セットが閉じられるようになります。rows.Next()
を使用して、次の行が存在するかどうかを確認します。存在する場合は、alb
というAlbum
型の変数を宣言し、rows.Scan()
を使用して列データをalb
のフィールドに割り当てます。具体的には、alb.ID
には結果セットのID列の値が、alb.Title
にはタイトル列の値が、alb.Artist
にはアーティスト列の値が、alb.Price
には価格列の値が代入されます。alb
をalbums
スライスに追加します(albums = append(albums, alb)
)。rows.Err()
を使用して、ループ終了時に発生したエラーをチェックします。もしエラーがあれば、エラーメッセージを返して関数を終了します。- 最後に、結果として取得したアルバムのスライス
albums
を返します。
この関数は、指定されたアーティスト名に一致するアルバムをデータベースから取得し、それらをAlbum
型のスライスとして返します。また、エラーが発生した場合には適切なエラーメッセージを返します。
albumByID メソッド
このコードは、指定されたIDを持つアルバムを検索するための関数「albumByID」を定義しています。
関数の引数は「id」というint64型の変数で、検索するアルバムのIDを指定します。
関数内で行われる主要な処理は次のとおりです:
alb
というAlbum
型の変数を宣言します。この変数は、検索結果のアルバムデータを格納するために使用されます。db.QueryRow
関数を使用して、指定されたIDに一致する行をデータベースから取得します。SQLクエリは「SELECT * FROM album WHERE id = ?」となっており、プレースホルダー「?」にはid
が代入されます。row.Scan
を使用して、クエリの結果セットから列データをalb
のフィールドに割り当てます。具体的には、alb.ID
には結果セットのID列の値が、alb.Title
にはタイトル列の値が、alb.Artist
にはアーティスト列の値が、alb.Price
には価格列の値が代入されます。row.Scan
の戻り値であるerr
をチェックします。もしerr
がsql.ErrNoRows
であれば、指定されたIDに対応するアルバムが存在しないことを示すエラーメッセージを返します。- もし
err
がsql.ErrNoRows
以外のエラーであれば、エラーメッセージを返します。 - 最後に、検索結果のアルバムデータが格納された
alb
を返します。
この関数は、指定されたIDに一致するアルバムをデータベースから取得し、それをAlbum
型の変数として返します。もし指定されたIDに対応するアルバムが存在しない場合には適切なエラーメッセージを返します。
addAlbum メソッド
このコードは、指定されたアルバムをデータベースに追加するための関数「addAlbum」を定義しています。
関数の引数は「alb」というAlbum
型の変数で、追加するアルバムの情報を持っています。
関数内で行われる主要な処理は次のとおりです:
db.Exec
関数を使用して、指定されたアルバムの情報をデータベースに挿入します。SQLクエリは「INSERT INTO album (title, artist, price) VALUES (?, ?, ?)」となっており、プレースホルダー「?」にはalb.Title
、alb.Artist
、alb.Price
の値が代入されます。db.Exec
の戻り値であるresult
は、実行結果を表すsql.Result
型のオブジェクトです。- エラーチェックを行います。もしエラーが発生した場合は、エラーメッセージを返して関数を終了します。
result.LastInsertId
を使用して、新しいエントリのアルバムIDを取得します。これにより、データベースが自動的に生成したアルバムのIDを取得できます。- エラーチェックを行います。もしエラーが発生した場合は、エラーメッセージを返して関数を終了します。
- 最後に、新しいエントリのアルバムIDを返します。
この関数は、指定されたアルバムをデータベースに追加し、新しいエントリのアルバムIDを返します。もし追加中にエラーが発生した場合には適切なエラーメッセージを返します。
おわりに
前回と今回の記事では、Golangを使ってリレーショナル・データベースで簡単なアクションを実行することができました。
本記事で使用したコードは下記のリポジトリにあります。
Golangが初めての方は、Effective Go と How to write Go codeに役立つベストプラクティスが記載されています。
また、Go ツアーというGolang の基礎をステップバイステップで学べる入門サイトもあります。
何か質問や相談があれば、遠慮なくコメントしてください。また、エンジニア案件についても、いつでも相談にのっていますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント