はじめに

前回の記事の続きです。
前回の記事はこちら

MySQL データベースの使用

今回は MySQL のデータベースをGoで使用する手順についてです。

環境構築

今回からは、Docker を使用して実行環境を用意します。

  1. 下記のリポジトリをクローンしてください。
    https://github.com/katsuobushiFPGA/docker-golang-db-for-develop.git

  2. 任意のディレクトリにクローンした後、Docker コンテナを立ち上げます。

1
docker-compose up -d
  1. 立ち上がったら、workspace コンテナに入ります。
1
docker-compose exec workspace bash
  1. main.go を サンプルとして作成して、下記を実行します。
1
go run main.go
1
2
3
4
5
6
7
// サンプル用
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 で書かれています。

同じものを使っていきましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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 を使いたいのでセットアップしておきます。

1
2
$ go mod init k-bushi.com/example
go: creating new go.mod: module k-bushi.com/example

Table を作成したいので、phpMyAdminmysql コンテナに入って SQL を実行します。
今回はmysqlコンテナに入ります。

1
2
3
4
$ docker-compose exec mysql bash
## mysql -u root -proot

SQLを実行します。

結果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 に貼り付けてみます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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 コンテナに接続するので接続先を修正したりしてます。

実行結果です!

1
2
3
4
5
6
7
8
9
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 が定義するデータベース接続と設定情報です。以下のシンタックスをサポートします:

なるほど?
接続先を変更する必要がありましたので、個々は変更しましたね!

1
2
3
4
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 の操作はすんなりいけそうです!

今回はここで終わり・・・ではなく、 phpMyAdminINSERTされているのかや UPDATE されているのかは確認していませんね?
ここを確認しましょう。

サンプルではデータを削除してしまっているので、
INSERT × 10 回 (id1~id10)
UPDATE (id:1)
DELETE (id:2)
SELECT (全部)
を順に実行して、最後どうなっているのかを確認したいと思います。
サンプルを改造しましょう!

まずは、mysql コンテナに入って、下記を実行します。(一旦全部消します。)

1
2
3
4
5
6
7
mysql> truncate userdetail;
Query OK, 0 rows affected (0.05 sec)

mysql> truncate userinfo;
Query OK, 0 rows affected (0.03 sec)

mysql>

消えたことの確認 (userdetail は使っていませんが。。) phpmyadmin-01 phpmyadmin-02

そして改造しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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)
    }
}

どどん!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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# 

phpmyadmin-03 phpmyadmin-04

おぉ~!
ちゃんとうまく行ってますね。

おわりに

明日は posgresql ですが、環境がまだ作れていません。
やります。
近況はこの後書きます。