中堅プログラマーの備忘録

忘れっぽくなってきたので備忘録として・・・

【Golang】MySQLの基本的な操作を行う

1.概要

案件に対する要件定義を行っていると
これはどの言語で組むのが最適なんだろうか・・・
と悩むエンジニアさんは多いのではないでしょうか?

正直どんな言語でも仕様を満たすことは可能かとは思うのですが
工数や拡張性、今後の世の中の動向などはどうしても考慮しなければいけません。
この機能はこの言語で実装して、ここはこの言語でなんてやってると
無限ループにはまりそうになります。

なぜこんなに悩むようになったのかというと
プログラミング言語はなんと【250言語】も存在するらしいのです。

そんな中、自分の経験値では測れない便利な言語も存在しているので
常にアンテナを張ってないと時代に取り残されてしまう
と思う今日この頃です。


今回は今、最も人気のある言語といってもいい【Golang】を使ってみようと思います。


2.【Golang】とは

Googleが開発したオープンソースプロジェクトのプログラミング言語です。
静的プログラミング言語の為、コンパイルが必要ですが
その分高速で動作します。
記述がシンプルであり、初心者向けでもあるといったイメージです。

私が一番メリットだと感じた部分が【Goルーチン】という
シンプルな並列処理があることです。
これにより、面倒な並列処理が簡単に書けるようになっています。


デメリットを調べてみると
①Genericsが存在しない
②例外処理がない
③コードの継承がない
等が多く挙げられていましたが、
この程度は【慣れ】だと思っていますので
特にデメリットという程の事ではないでしょう。


3.MySQLに接続する

では本題である【Golang】でMySQLを操作してみます。
開発環境はVSCodeを使いました。

今回テストに使用するデータベースは下記のとおりです。

項目
DB名 go_test
テーブル名 user
ユーザー名 gouser
パスワード hogehoge
ホスト localhost
ポート 3306
mysql> SHOW COLUMNS FROM user;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int         | NO   | PRI | NULL    | auto_increment |
| name  | varchar(50) | YES  |     | NULL    |                |
| age   | int         | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+


まずは【Go-MySQL-Driver】を下記のコマンドでインストールします。

go get -u github.com/go-sql-driver/mysql


コードは下記のとおりです。

package main

import (
	"database/sql"
	"log"

	_ "github.com/go-sql-driver/mysql"
)

func main() {
	connectOnly()
}

func connectOnly() {
	// データベースのハンドルを取得する
	db, err := sql.Open("mysql", "gouser:hogehoge@tcp(localhost:3306)/go_test")
	if err != nil {
		// ここではエラーを返さない
		log.Fatal(err)
	}
	defer db.Close()

	// 実際に接続する
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	} else {
		log.Println("データベース接続完了")
	}
}

コンソールに

2021/07/13 14:20:40 データベース接続完了

と出力されたら問題なく接続できたということになります。


4.データを追加する(INSERT)

早速データを追加してみます。
追加するデータは下記のとおりです。

項目
id 1
name chuken
age 38

最終的な結果として【LastInsertId】で挿入したレコードのキーを取得しています。
必要があれば【RowsAffected】で影響を受ける行の数を取得してもいいかと思ます。

func sqlInsert() {
	// データベースのハンドルを取得する
	db, err := sql.Open("mysql", "gouser:hogehoge@tcp(localhost:3306)/go_test")
	if err != nil {
		// ここではエラーを返さない
		log.Fatal(err)
	}
	defer db.Close()

	// SQLの準備
	ins, err := db.Prepare("INSERT INTO user VALUES(?, ?, ?)")
	if err != nil {
		log.Fatal(err)
	}
	defer ins.Close()

	// SQLの実行
	res, err := ins.Exec(1, "chuken", 38)
	if err != nil {
		log.Fatal(err)
	}

	// 結果の取得
	lastInsertID, err := res.LastInsertId()
	if err != nil {
		log.Fatal(err)
	}
	log.Println(lastInsertID)
}


結果は下記のとおり問題なく登録出来ました。

mysql> select * from user;
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | chuken |   38 |
+----+--------+------+

5.データを変更する(UPDATE)

下記のとおりデータを変更します。

項目
id 1
name chuken
age 39
func sqlUpdate() {
	// データベースのハンドルを取得する
	db, err := sql.Open("mysql", "gouser:hogehoge@tcp(localhost:3306)/go_test")
	if err != nil {
		// ここではエラーを返さない
		log.Fatal(err)
	}
	defer db.Close()

	// SQLの準備
	upd, err := db.Prepare("UPDATE user SET age = ? WHERE id = ?")
	if err != nil {
		log.Fatal(err)
	}
	defer upd.Close()

	// SQLの実行
	res, err := upd.Exec(39, 1)
	if err != nil {
		log.Fatal(err)
	}
}


結果は下記のとおりとなりました。

mysql> select * from user;
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | chuken |   39 |
+----+--------+------+

6.データを取得する(SELECT)

わかりやすいように取得したデータを入れる構造体を用意します。

// 取得したデータをいれる構造体を準備する
type Person struct {
	id   int
	name string
	age  int
}

func sqlSelect() {
	// データベースのハンドルを取得する
	db, err := sql.Open("mysql", "gouser:hogehoge@tcp(localhost:3306)/go_test")
	if err != nil {
		// ここではエラーを返さない
		log.Fatal(err)
	}
	defer db.Close()

	// SQLの実行
	rows, err := db.Query("SELECT * FROM user")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	// SQLの実行
	for rows.Next() {
		var person Person
		err := rows.Scan(&person.id, &person.name, &person.age)

		if err != nil {
			panic(err.Error())
		}
		log.Println(person.id, person.name, person.age)

	}
}


コンソールに取得したデータが出力されます。

2021/07/13 14:20:40 1 chuken 39

7.データを削除する(DELETE)

func sqlDelete() {
	// データベースのハンドルを取得する
	db, err := sql.Open("mysql", "gouser:hogehoge@tcp(localhost:3306)/go_test")
	if err != nil {
		// ここではエラーを返さない
		log.Fatal(err)
	}
	defer db.Close()

	// SQLの実行
	del, err := db.Prepare("DELETE FROM user WHERE id = ?")
	if err != nil {
		log.Fatal(err)
	}
	defer del.Close()

	// SQLの実行
	res, err := del.Exec(1)
	if err != nil {
		log.Fatal(err)
	}

	// 結果の取得(影響を受ける行の数を取得)
	affected, err := res.RowsAffected()
	if err != nil {
		log.Fatal(err)
	}
	log.Println(affected)
}

結果は下記のとおりとなりました。

mysql> select * from user;
Empty set (0.00 sec)

8.まとめ

これでひととおりの処理は問題なく出来ました。
使ってみた感想ですが、非常にわかりやすく、人気があるのも頷けました。
実際の運用ではトランザクション処理などは必要になるとは思いますが
それはまた。。。