Build Web Application With Golang を試してやってみる 19

はじめに

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

SQLite データベースの使用

今回は SQLite のデータベースをGoで使用する手順についてです。
SQLiteはファイルに書き込むものなので、準備が必要です。

前回と同様の環境を使用します。

準備

docker-compose up -d

##コンテナに入ります。
docker-compose exec workspace bash

bash-5.0# cd /tmp
bash-5.0# pwd
/tmp

bash-5.0# sqlite3 foo.db
CREATE TABLE `userinfo` (
    `uid` INTEGER PRIMARY KEY AUTOINCREMENT,
    `username` VARCHAR(64) NULL,
    `departname` VARCHAR(64) NULL,
    `created` DATE NULL
);

CREATE TABLE `userdeatail` (
    `uid` INT(10) NULL,
    `intro` TEXT NULL,
    `profile` TEXT NULL,
    PRIMARY KEY (`uid`)
);
sqlite> CREATE TABLE `userinfo` (
   ...>     `uid` INTEGER PRIMARY KEY AUTOINCREMENT,
   ...>     `username` VARCHAR(64) NULL,
   ...>     `departname` VARCHAR(64) NULL,
   ...>     `created` DATE NULL
   ...> );
 TEXT NULL,
    PRIMARY KEY (`uid`)
);sqlite> 
sqlite> CREATE TABLE `userdeatail` (
   ...>     `uid` INT(10) NULL,
   ...>     `intro` TEXT NULL,
   ...>     `profile` TEXT NULL,
   ...>     PRIMARY KEY (`uid`)
   ...> );
sqlite> 

うまく行っているみたいです。

sqlite> .tables
userdeatail  userinfo 

うまく行っていますね!
これでOKです。

ドライバ

SQLiteはオープンソースの組み込み式リレーショナルデータベースです。
独立しており、設定なしでトランザクションのSQLデータベースエンジンをサポートします。
非常にポータブルで簡単に利用でき、コンパクトで効率が高く、信頼性があります。
他のデータベース管理システムとは異なり、SQLiteのインストールと実行は非常に簡単です。
多くの場合は、ただSQLiteのバイナリファイルを用意するだけですぐに作成、接続、使用することができます。
もしあなたが現在組み込み式データベースかソリューションをお探しであれば、SQLiteは絶対に考慮するに値します。SQLiteはいわばオープンソースのAccessのようなものです。

MySQL とかと違って、サーバ立てずに書き込みしたりできるのが特徴ですね。
このお手軽な感じはとても好きです。
SQLite はモバイルアプリとかで使われていたりしますね。
過去Androidアプリを作成したときは使用しました。

Goがサポートするsqliteのドライバも比較的多いのですが、大部分はdatabase/sqlインターフェースをサポートしていません。

今回は下記を使用するようです。
のっかりましょう。

https://github.com/mattn/go-sqlite3 database/sqlインターフェースをサポートしています。cgo(cgoに関する情報はオフィシャルドキュメントかこの本の最後の章をご参考ください)に基づいて記述されています。

実例コード

SQLite のDBはできているので、早速プログラムを試していきます。

前回のプロジェクトでは、 main.go につくっていたのですが、

main.gomysql.goとしています。
新しく作成するファイルは、 sqlite.go としています。

早速下記で試してみます。

package main

import (
    "database/sql"
    "fmt"
    "time"
    _ "github.com/mattn/go-sqlite3"
)

func main() {
    db, err := sql.Open("sqlite3", "/tmp/foo.db")
    checkErr(err)

    //データの挿入
    stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)")
    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 time.Time
        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)
    }
}
bash-5.0# go run sqlite.go 
go: finding github.com/mattn/go-sqlite3 v2.0.2+incompatible
go: downloading github.com/mattn/go-sqlite3 v2.0.2+incompatible
go: extracting github.com/mattn/go-sqlite3 v2.0.2+incompatible
1
1
1
astaxieupdate
研究開発部門
2012-12-09 00:00:00 +0000 UTC
1
bash-5.0# 

できているっぽいですね!

さてこちらも、前回と同様、魔改造して同じことをしてみましょう。

package main

import (
    "database/sql"
    "fmt"
    "time"
    _ "github.com/mattn/go-sqlite3"
)

func main() {
    db, err := sql.Open("sqlite3", "/tmp/foo.db")
    checkErr(err)

    //データの挿入
    stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)")
    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)
    fmt.Println("RowsAffected: ", affect)

    //データの検索
    rows, err := db.Query("SELECT * FROM userinfo")
    checkErr(err)

    for rows.Next() {
        var uid int
        var username string
        var department string
        var created time.Time
        err = rows.Scan(&uid, &username, &department, &created)
        checkErr(err)
        fmt.Println(uid)
        fmt.Println(username)
        fmt.Println(department)
        fmt.Println(created)
    }
    db.Close()
}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}

下記のような感じになりました。
truncate , SQLiteだと DELETE + VACUUM;?していないので、
uid はリセットされていませんね。 (AUTO_INCREMENTで保持されている値)

bash-5.0# go run sqlite.go 
LastInsertId:  2
LastInsertId:  3
LastInsertId:  4
LastInsertId:  5
LastInsertId:  6
LastInsertId:  7
LastInsertId:  8
LastInsertId:  9
LastInsertId:  10
LastInsertId:  11
RowsAffected:  0
RowsAffected:  1
3
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
4
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
5
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
6
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
7
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
8
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
9
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
10
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
11
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
bash-5.0# 
bash-5.0# sqlite3 /tmp/foo.db 
SQLite version 3.30.1 2019-10-10 20:19:45
Enter ".help" for usage hints.
sqlite> .ta 
userdeatail  userinfo   
sqlite> select * from userinfo;
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
11|astaxie|研究開発部門|2012-12-09
sqlite>

こんな感じになっています。
おっと、uid 値がリセットされていないので、 id:1 で更新している部分はできていないですね!
こういうのには気をつけましょう。
一応

## truncate相当
## テーブルの中身全部削除してきれいにする
DELETE FROM userinfo;
VACUUM;
## テーブルシーケンスの削除
DELETE FROM sqlite_sequence where name='userinfo';

をした後に実行した結果も貼り付けておきます。

bash-5.0# go run sqlite.go
LastInsertId:  1
LastInsertId:  2
LastInsertId:  3
LastInsertId:  4
LastInsertId:  5
LastInsertId:  6
LastInsertId:  7
LastInsertId:  8
LastInsertId:  9
LastInsertId:  10
RowsAffected:  1
RowsAffected:  1
1
Update(id:1)
研究開発部門
2012-12-09 00:00:00 +0000 UTC
3
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
4
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
5
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
6
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
7
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
8
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
9
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
10
astaxie
研究開発部門
2012-12-09 00:00:00 +0000 UTC
bash-5.0# 
sqlite> select * from userinfo;
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

よし!

コードのハイライトがちょっと見にくいのどうにかしたいですね。
※どうにかしました。 (前よりかは良くなっていると思います。)

おわりに

前回とやっていることは同じ記事なので、SQLiteは別にいいよとか、一個わかればよいという方には必要ない記事でした。
自分の理解も兼ねて全部やっているので、ご承知ください。
次回は PostgreSQL です!

Hugo で構築されています。
テーマ StackJimmy によって設計されています。