はじめに
前回の記事の続きです。
前回の記事はこちら
MySQL データベースの使用
今回は MySQL のデータベースをGo
で使用する手順についてです。
環境構築
今回からは、Docker
を使用して実行環境を用意します。
下記のリポジトリをクローンしてください。
https://github.com/katsuobushiFPGA/docker-golang-db-for-develop.git任意のディレクトリにクローンした後、
Docker
コンテナを立ち上げます。
docker-compose up -d
- 立ち上がったら、
workspace
コンテナに入ります。
docker-compose exec workspace bash
main.go
を サンプルとして作成して、下記を実行します。
go run main.go
// サンプル用
package main
import "fmt"
func main() {
fmt.Println("Hello, World")
}
構成について
このリポジトリは下記のような構成になっております。
workspace
コンテナ… Go
を実行するコンテナ
mysql
コンテナ… mysql
を実行するコンテナ
phpmyadmin
コンテナ…phpmyadmin
を実行するコンテナ
さて、これで MySQL
を試せる環境がやってきました。
早速読んでいきましょう。
MySQL ドライバ
MySQLドライバ
もいくつかあるので、その中の一つを使っていくようです。
https://github.com/go-sql-driver/mysql database/sql をサポートしており、すべて go で書かれています。
同じものを使っていきましょう。
CREATE DATABASE `gotest`;
USE `gotest`;
CREATE TABLE `userinfo` (
`uid` INT(10) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(64) NULL DEFAULT NULL,
`departname` VARCHAR(64) NULL DEFAULT NULL,
`created` DATE NULL DEFAULT NULL,
PRIMARY KEY (`uid`)
);
CREATE TABLE `userdetail` (
`uid` INT(10) NOT NULL DEFAULT '0',
`intro` TEXT NULL,
`profile` TEXT NULL,
PRIMARY KEY (`uid`)
);
同じテーブルを作成してみます。
Go modules
を使いたいのでセットアップしておきます。
$ go mod init k-bushi.com/example
go: creating new go.mod: module k-bushi.com/example
Table
を作成したいので、phpMyAdmin
か mysql
コンテナに入って SQL を実行します。
今回はmysql
コンテナに入ります。
$ docker-compose exec mysql bash
## mysql -u root -proot
SQLを実行します。
結果
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| gotest |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.01 sec)
mysql> use gotest;
Database changed
mysql> show tables;
+------------------+
| Tables_in_gotest |
+------------------+
| userdetail |
| userinfo |
+------------------+
2 rows in set (0.01 sec)
できました!
そしたら、下にあるコードを main.go
に貼り付けてみます。
package main
import (
_ "github.com/go-sql-driver/mysql"
"database/sql"
"fmt"
//"time"
)
func main() {
db, err := sql.Open("mysql", "root:root@tcp(mysql:3306)/gotest?charset=utf8")
checkErr(err)
//データの挿入
stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
checkErr(err)
res, err := stmt.Exec("astaxie", "研究開発部門", "2012-12-09")
checkErr(err)
id, err := res.LastInsertId()
checkErr(err)
fmt.Println(id)
//データの更新
stmt, err = db.Prepare("update userinfo set username=? where uid=?")
checkErr(err)
res, err = stmt.Exec("astaxieupdate", id)
checkErr(err)
affect, err := res.RowsAffected()
checkErr(err)
fmt.Println(affect)
//データの検索
rows, err := db.Query("SELECT * FROM userinfo")
checkErr(err)
for rows.Next() {
var uid int
var username string
var department string
var created string
err = rows.Scan(&uid, &username, &department, &created)
checkErr(err)
fmt.Println(uid)
fmt.Println(username)
fmt.Println(department)
fmt.Println(created)
}
//データの削除
stmt, err = db.Prepare("delete from userinfo where uid=?")
checkErr(err)
res, err = stmt.Exec(id)
checkErr(err)
affect, err = res.RowsAffected()
checkErr(err)
fmt.Println(affect)
db.Close()
}
func checkErr(err error) {
if err != nil {
panic(err)
}
}
Docker
で立てた mysql
コンテナに接続するので接続先を修正したりしてます。
実行結果です!
bash-5.0# go run main.go
1
1
1
astaxieupdate
研究開発部門
2012-12-09
1
bash-5.0#
やったぜ!
また読みすすめていきます。
キーとなるいくつかの関数についてご説明します:
sql.Open()関数は登録済みのデータベースドライバを開くために使用されます。
go-sql-driver の中で mysql のデータベースドライバを登録し、2つ目の引数は DSN(Data Source Name)です。
これは go-sql-driver が定義するデータベース接続と設定情報です。以下のシンタックスをサポートします:
なるほど?
接続先を変更する必要がありましたので、個々は変更しましたね!
user@unix(/path/to/socket)/dbname?charset=utf8
user:password@tcp(localhost:5555)/dbname?charset=utf8
user:password@/dbname
user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname
db.Prepare()関数は sql 操作を実行するプリペアードステートメントを返すために用いられます。
その後、準備完了の実行状態を返します。
db.Query()関数は直接 Sql を実行し Rows 結果を返すために使われます。
stmt.Exec()関数は stmt が用意された SQL 文を実行するために用いられます。
渡される引数がどれも=?に対応するデータであることがわかるかとおもいます。このような方法である程度 SQL インジェクションを防止することができます。
このあたりはどの言語でも共通ですね。
↑ プリペアードステートメント
覚えておくと、どの言語でも DB の操作はすんなりいけそうです!
今回はここで終わり・・・ではなく、
phpMyAdmin
で INSERT
されているのかや UPDATE
されているのかは確認していませんね?
ここを確認しましょう。
サンプルではデータを削除してしまっているので、INSERT
× 10 回 (id1~id10)UPDATE
(id:1)DELETE
(id:2)SELECT
(全部)
を順に実行して、最後どうなっているのかを確認したいと思います。
サンプルを改造しましょう!
まずは、mysql
コンテナに入って、下記を実行します。(一旦全部消します。)
mysql> truncate userdetail;
Query OK, 0 rows affected (0.05 sec)
mysql> truncate userinfo;
Query OK, 0 rows affected (0.03 sec)
mysql>
消えたことの確認 (userdetail
は使っていませんが。。)
そして改造しました。
package main
import (
_ "github.com/go-sql-driver/mysql"
"database/sql"
"fmt"
//"time"
)
func main() {
db, err := sql.Open("mysql", "root:root@tcp(mysql:3306)/gotest?charset=utf8")
checkErr(err)
//データの挿入
stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
checkErr(err)
var res sql.Result
for i := 0; i < 10; i++ {
res, err = stmt.Exec("astaxie", "研究開発部門", "2012-12-09")
checkErr(err)
id, err := res.LastInsertId()
checkErr(err)
fmt.Println("LastInsertId:", id)
}
//データの更新
stmt, err = db.Prepare("update userinfo set username=? where uid=?")
checkErr(err)
res, err = stmt.Exec("Update(id:1)", 1)
checkErr(err)
affect, err := res.RowsAffected()
checkErr(err)
fmt.Println("RowsAffected", affect)
//データの削除
stmt, err = db.Prepare("delete from userinfo where uid=?")
checkErr(err)
res, err = stmt.Exec(2)
checkErr(err)
affect, err = res.RowsAffected()
checkErr(err)
//データの検索
rows, err := db.Query("SELECT * FROM userinfo")
checkErr(err)
for rows.Next() {
var uid int
var username string
var department string
var created string
err = rows.Scan(&uid, &username, &department, &created)
checkErr(err)
fmt.Println(uid)
fmt.Println(username)
fmt.Println(department)
fmt.Println(created)
}
fmt.Println("RowsAffected", affect)
db.Close()
}
func checkErr(err error) {
if err != nil {
panic(err)
}
}
どどん!
bash-5.0# go run main.go
LastInsertId: 1
LastInsertId: 2
LastInsertId: 3
LastInsertId: 4
LastInsertId: 5
LastInsertId: 6
LastInsertId: 7
LastInsertId: 8
LastInsertId: 9
LastInsertId: 10
RowsAffected 1
1
Update(id:1)
研究開発部門
2012-12-09
3
astaxie
研究開発部門
2012-12-09
4
astaxie
研究開発部門
2012-12-09
5
astaxie
研究開発部門
2012-12-09
6
astaxie
研究開発部門
2012-12-09
7
astaxie
研究開発部門
2012-12-09
8
astaxie
研究開発部門
2012-12-09
9
astaxie
研究開発部門
2012-12-09
10
astaxie
研究開発部門
2012-12-09
RowsAffected 1
bash-5.0#
おぉ~!
ちゃんとうまく行ってますね。
おわりに
明日は posgresql
ですが、環境がまだ作れていません。
やります。
近況はこの後書きます。