読者です 読者をやめる 読者になる 読者になる

かけ足で学ぶ Golang その3 〜データベースの準備〜


f:id:Naotsugu:20170417231928p:plain

前回は Go で簡単なWebサーバを作成しました。

etc9.hatenablog.com

今回からはデータベース操作を行っていきます。

データベースの準備

今回はデータベースサーバに H2 を使います。 H2 は postgres プロトコル互換モードがあるので、postgres 用のドライバで接続できます。

H2 は単一 jar を java -cp で起動すれば使えますが、ここではダウンロードから起動までの操作も Go で書いてみます。

ファイルのダウンロード

ファイルの取得は http.Get(url) でできます。 以下のような流れになります。

out, err := os.Create(path)
resp, err := http.Get(url)
n, err := io.Copy(out, resp.Body)

出力対象のファイルを作成し、Get で取得したレスポンスをファイルにコピーします。

全体は以下のようになります。

func download(url string, path string) error {

    out, err := os.Create(path)
    if err != nil {
        return err
    }
    defer out.Close()

    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    if _, err := io.Copy(out, resp.Body); err != nil {
        return err
    }

    return nil
}

defer は java での finally のようなものです。メソッドを抜ける時に定義したのと逆順で処理が実行されます。

主にリソースの開放などに使われます。注意点としてはループ処理の中で defer を定義してはいけない点です。

if はセミコロンで区切ることで、条件句以外に文を書くことができます。 io.Copy() の結果として err を受け取り(第一戻り値は無視)、err の有無を条件式で判定しています。

コマンドの実行

H2 をダウンロードして実行します。 コマンドの実行は exec.Command() を使います。

const (
    url  = "http://central.maven.org/maven2/com/h2database/h2/1.4.194/h2-1.4.194.jar"
    path = "lib/h2-1.4.194.jar"
)

func startH2() {

    if _, err := os.Stat(path); os.IsNotExist(err) {
        os.MkdirAll(filepath.Dir(path), 0755)
        if err := download(url, path); err != nil  {
            panic(err)
        }
    }

    cmd := exec.Command("java", "-cp",  path, "org.h2.tools.Server")
    cmd.Start()
    time.Sleep(3 * time.Second)
    log.Printf("PID[%v]", cmd.Process.Pid)

}

最初にファイルの存在チェックを行い、ファイルが無ければダウンロードしています。 失敗時には panic() でアプリケーションを終了します。

その後、コマンドを実行します。cmd.Run() とすると処理の終了を待ちますが、ここでは cmd.Start() でコマンドの終了を待たずに処理を進めます。

H2 のストップは以下のコマンドで行います。

func stopH2() {
    err := exec.Command("java", "-cp",  path,
        "org.h2.tools.Server", "-tcpShutdown", "tcp://localhost:9092").Run()
    if err != nil {
        log.Fatal(err)
    }
}

まとめ

ここまでの全体像は以下となります。

package main

import (
    "log"
    "os"
    "net/http"
    "io"
    "os/exec"
    "path/filepath"
    "time"
)

const (
    url  = "http://central.maven.org/maven2/com/h2database/h2/1.4.194/h2-1.4.194.jar"
    path = "lib/h2-1.4.194.jar"
)

func download(url string, path string) error {

    out, err := os.Create(path)
    if err != nil {
        return err
    }
    defer out.Close()

    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    if _, err := io.Copy(out, resp.Body); err != nil {
        return err
    }

    return nil
}


func startH2() {

    if _, err := os.Stat(path); os.IsNotExist(err) {
        os.MkdirAll(filepath.Dir(path), 0755)
        if err := download(url, path); err != nil  {
            panic(err)
        }
    }

    cmd := exec.Command("java", "-cp",  path, "org.h2.tools.Server")
    cmd.Start()
    time.Sleep(3 * time.Second)
    log.Printf("H2 Started. PID[%v]", cmd.Process.Pid)
}


func stopH2() {
    err := exec.Command("java", "-cp",  path,
        "org.h2.tools.Server", "-tcpShutdown", "tcp://localhost:9092").Run()
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    startH2()
    stopH2()
}

データベースの起動と停止ができたので、次回でデータベースへのアクセスを行います。

etc9.hatenablog.com