SQLiteのジャーナルモード

2つのSQLiteのデータベースファイルがあり、それぞれ更新すると一方は「-journal」ファイルが作成されて、もう一方は「-shm」と「-wal」ファイルが作成されるという違うファイルが作成される理由がわからず調べたら、ジャーナルモードが違ったというお話。

ジャーナルモードの大分

SQLiteのジャーナルモードは大きく分けて「delete」と「wal」がある。
「delete」の時は「-journal」ファイルが作成され、「wal」の時は「-shm」と「-wal」ファイルが作成される。
例えば「foo」というデータベースファイルがあると、ジャーナルモードが「delete」の時は「foo」というファイルの他に「foo-journal」というファイルが作成され、ジャーナルモードが「wal」の時は「foo-shm」と「foo-wal」というファイルが作成される。
どちらもジャーナルファイルなので、本体のデータベースにコミットするまでのデータが書き込まれるという意味では同じだが、動作は若干異なる。

ジャーナルモードの詳細は本家のサイトを参照。
http://www.sqlite.org/pragma.html#pragma_journal_mode

deleteモード

トランザクション開始から終了までの更新内容を「-journal」ファイルに書き込み、コミットしたときに本体のデータベースファイルへ更新内容を反映し、反映したら「-journal」ファイルを削除する。
最後の「-journal」ファイルを削除する動作に因んで「deleteモード」と呼んでいる。
コミットした時点でデータベースファイルを更新するため、大きなデータベースファイルの場合は書き込みに時間がかかるかもしれない。(未検証)

walモード

トランザクション開始から終了までの更新内容を「-shm」ファイルに書き込み、コミットした時に「-wal」ファイルへ更新内容を書き込む。
コミットした時点では本体データベースファイルには更新内容を書き込まないため、本体に比べて小さいファイルの更新でトランザクションを完了できるメリットがある。
なのでデータ更新が多いシステムではwalモードの方が有利と言える。(未検証)
ただしデータが本体データベースファイルとwalファイルに分散されるため、データの取得には時間がかかるかもしれない。(未検証)

walファイルはデータベース接続を閉じる時に、その内容を本体データベースファイルに反映する。
つまりクローズ処理はdeleteモードに比べて時間がかかる。

永続性の違い

先の通り、deleteモードはコミットした時点で本体データベースに更新を反映する一方、walモードではコミットしても本体データベースに更新を反映しない。
そのため、もしコミット後にシステムが異常終了等した場合、walモードでは本体データベースに更新を反映するためのリカバリが必要になる。

その他のジャーナルモード

上の2つ以外にも以下のようなジャーナルモードがある。

truncateモード

deleteモードの亜種で、コミットしても「-journal」ファイルを削除せず、0バイトのファイルを残しておく。
ファイル作成時の権限等のリスクを減らすには有利かもしれない。
データベースを閉じて再度接続するとdeleteモードになる。

persistモード

deleteモードの亜種で、コミットしても「-journal」ファイルの内容を削除せずに取っておく。
データベースを閉じて再度接続するとdeleteモードになる。

memoryモード

ジャーナルをファイルではなくメモリで管理する。
とにかく動作を速くしたい場合は選択する価値があるかもしれないが、メモリの容量に注意が必要かもしれない。
データベースを閉じて再度接続するとdeleteモードになる。

offモード

ジャーナリングしない・・・ということか?(未検証)

まとめ

deleteモードかwalモードはそれぞれメリット・デメリットがあるので、システムの特性に合わせて適切に使い分けましょう。

DAG (Directed acyclic graph) - 有向非巡回グラフ

先日、JJUG CCC 2015 Fallに参加して、灰色のサイトで有名なひしだまさん(ひしだま (@hishidama) | Twitter)の「GH-6 Java8 Stream APIとApache SparkとAsakusa Frameworkの類似点・相違点」を聞いた際に出てきた「DAG (Directed acyclic graph) - 有向非巡回グラフ」が面白そうだったので調べてみた。

DAGとは

Wikiによると、

グラフ理論における閉路のない有向グラフの事。
有向グラフは頂点と有向辺(方向を示す矢印付きの辺)からなり、辺は頂点同士をつなぐが、ある頂点 v から出発し、辺をたどり、頂点 v に戻ってこないのが有向非巡回グラフである。

wikipedia:有向非巡回グラフ

となっている。

DAGの例

Wikiの例はわかりづらいので、JJUG CCC 2015 Fallのひしだまさんのスライドを見ると、18枚目に例が載っている。

左は向きがない(無向)なのでDAGではない。
中央は菱型の条件分岐で上に戻っている(巡回)のでDAGではない。
右は向きがあって(有向)条件分岐があるが循環していない(非巡回)のでDAGであると言える。

ひしだまさんの発表は、DAGで例題を示し、Stream APIとSparkとAsakusaFWの相違点について説明するという流れになっていた。
説明も聞いていたが、自分はこのDAGが気になってしまって、心の半分がDAGに持って行かれていたことは内緒としておきたい・・・。

DAGの活用

DAGを使うと、今まで言葉でしか説明していなかったものや、なんとなく図式化していたものを明確に説明できるのではないだろうか。

例えば

社員一覧画面は、社員マスタからデータを取得し表示する。
ただし所属は所属マスタから取得し表示すること。

のようなものは下記のように表せる。
f:id:nave_kazu:20151130154420p:plain

うん。わかりやすい。


もう少し複雑な例だと日締め処理のバッチなど。

その日一日の取引内容を集計する。
集計結果はPDF化し担当部署長宛にメールにて通知する。

のようなものは下記のように表せる。
f:id:nave_kazu:20151130154422p:plain

ここまでやってて、もっとDAGを用いるのに適した分野があることに気付いた。
そう、料理のレシピだ。

DAGを用いた料理のレシピ

例えばみんなのきょうの料理に掲載されている「かじきのガーリック炒めレシピ」だと、このようになる。
f:id:nave_kazu:20151130154424p:plain

ちなみにカレーのレシピはこのようになる。
f:id:nave_kazu:20151130154427p:plain

このように、DAGの応用範囲は広いということがわかった。
料理のレシピはDAGでも併せて記述するべきだと思う。

JavaのStream API(IntStream)を触ってみた

調子に乗ってIntStreamも使ってみた。

UNIX/Linux系アプリにありがちな、実行時引数に「-」を使って色々なパラメータを渡すアレを受け取る処理を書いてみた。

例題として以下のパラメータを使用する。

  • -c → 設定ファイルの指定
  • -i → 入力ファイルの指定
  • -o → 出力ファイルの指定

例えば以下のようにパラメータを指定する。
FooApplication -c configFile -i inputFile -o outputFile

当然、順番を変えて以下のように指定しても同じ意味となる。
FooApplication -i inputFile -c configFile -o outputFile

Javaの場合、mainメソッドの引数「String[] args」にそれぞれの値が配列に格納されて渡ってくるので、今回はそれをIntStreamを使って処理をする。
処理結果はMapに詰めて、キーにスイッチの値( -c など)を、バリューにそのスイッチの値 (configFile)を詰めることにする。

コードで書いた処理結果のイメージは以下の通り。

    Map<String, String> map = new HashMap<>();

    map.put("-c", "configFile");
    map.put("-i", "inputFile");
    map.put("-o", "outputFile");

Stream APIを使わなかった場合は以下の通り。(諸々のエラー処理は省略)

    Map<String, String> map = new HashMap<>();

    for (int i=0; i<args.length; i++) {
        if (args[i].startsWith("-")) {
            map.put(args[i], args[i+1]);
        }
    }

これがStream APIを使うと以下の通り。(諸々のエラー処理は省略)

    Map<String, String> map = new HashMap<>();

    IntStream.range(0, args.length)
        .filter(i -> args[i].startsWith("-"))
        .forEach(i -> map.put(args[i], args[i+1]));

・・・たぶん、IntStreamはこういう使い方をするためのものではないよね。
うん。知ってて使ってみた。

JavaのStream APIを触ってみた

読み込んだテキストファイルのうち「#」で始まる行と空行を除外するという処理を、せっかくだからStream APIで実装してみた。

Stream APIを使わなかった場合は以下の通り。

    List<String> result = new ArrayList<>();

    // 対象のテキストファイルを読み込んで、各行の内容を含んだListを受け取る
    List<String> lines = readAllLines();

    for (String line: lines) {
        // #から始まる行は読み飛ばす
        if (line.trim().startsWith("#")) {
            continue;
        }

        // 空行も読み飛ばす
        if (line.trim().length()==0) {
            continue;
        }

        result.add(line);
    }

これがStream APIを使うと以下の通り。

    // 対象のテキストファイルを読み込んで、各行の内容を含んだListを受け取る
    List<String> lines = readAllLines();
    List<String> result = lines.stream()
            .filter(line -> !(line.trim().startsWith("#")))        // #から始まる行は読み飛ばす
            .filter(line -> !(line.trim().length()==0))            // 空行も読み飛ばす
            .collect(Collectors.toList());

実にシンプル。
これは気持ち良いかも。
for文禁止にしたい気持ちが、今更ながらわかってきた。

Gitの設定ファイル

以下のサイトを見ながら自分の言葉でまとめてみた。
Git - Git の設定

Gitの設定方法

下記のようにgitコマンドを使ってGitの設定を変更することが出来る。

git config [設定先] [設定項目] [設定値]


例えばユーザー名として「John Doe」を設定するなら、設定項目が「user.name」、設定値が「John Doe」となり、コマンドは以下のようになる。

git config --global user.name "John Doe"

これでグローバル域にユーザー名「John Doe」を設定したことになる。

あとは、設定先を切り替えることで使いやすい設定にカスタマイズすれば良い。

続いてその設定先についてまとめる。

3段構えの設定先

Gitの設定先は3段構えとなっている。

  • システム:インストールしたGit本体に対する設定
  • グローバル:ユーザー単位での設定
  • ローカル:リポジトリ単位での設定

上から順にスコープが狭くなる。

それぞれを掘り下げてみてみる。

システム

インストールしたGit本体に対する設定で、Linux系だと下記のファイルに設定内容が書き込まれる。

/etc/gitconfig

Windowsの場合はGitのインストールフォルダの下記のファイルに設定内容が書き込まれる。

[インストールフォルダ]/etc/gitconfig


gitコマンドはまずこのファイルに記載した設定を読み込む。

[設定先]に使用するパラメータは「--system」となり、以下のように設定する。

git config --system core.autocrlf true

グローバル

そのPCのユーザーごとの設定で、LinuxでもWindowsでもユーザーのホームディレクトの下記ファイルに設定内容が書き込まれる。

.gitconfig

gitコマンドはシステムで設定された内容に次いでこのファイルに記載した設定を読み込む。
重複した設定は上書きされる(はず。未確認)。

ユーザーごとの設定なので、先ほどの例に出したユーザー名「John Doe」の設定はここにしたほうが良い。

[設定先]に使用するパラメータは「--global」となり、ユーザー名以外を設定するなら以下のようになる。

git config --global user.email john@doe.com

ローカル

作業中のリポジトリのみの設定で、リポジトリフォルダの下記のファイルに設定内容が書き込まれる。

.git/config

gitコマンドはシステム、グローバルの順にで設定された内容を読み込み、最後にこのファイルに記載した設定を読み込む。

例えばメインで使用するユーザー名「John Doe」はグローバルに設定し、このリポジトリだけ異なるユーザー名「Jane Doe」で作業をするなら、その設定を下記のように行う。

git config --local user.name "Jane Doe"

ローカルだけは、パラメータ「--local」を省略することも可能。

まとめ

3つの設定をまとめると下記のようになる。

git config --system [システムへの設定]
git config --global [グローバルへの設定]
git config --local [ローカルへの設定]

設定できる項目や設定に関することについては冒頭のGitのサイトや下記コマンドで表示するマニュアルページに詳しくあるので、そちらを参照。

git config --help

GitBucket 3.2 を試す

Scalaで実装されたオープンソースGitHubクローン」のGitBucketのインストールが予想以上に簡単だったので、メモを残す。
今回入れるGitBucketの3.2。

GitBucketのサイトは下記の通り。
https://github.com/takezoe/gitbucket

事前準備

Scalaで実装されているので、Javaが入っていないとならない。
今回はJava8(Update31)で試した。
Javaのインストールフォルダのbinフォルダにパスを通しておきましょう)

続いてGitBucket本体のダウンロード。
GitBucketのwarファイルをGitBucketのサイトからダウンロードする。
GitBucket 3.2は下記URLでダウンロードできる。
https://github.com/takezoe/gitbucket/releases/download/3.2/gitbucket.war

準備に必要なのはこの2点。

GitBucketの実行

GitBucketの実行は下記のコマンドで行う。

java -jar gitbucket.war

これだけ。
GitBucketには組み込みWebサーバのJettyを使っているため、上記のようにJARファイルを実行するとGitBucketをデプロイ済みのWebサーバーが起動する。

GitBucketへアクセスする

ブラウザを起動して、起動したサーバーもしくはローカルホストのポート8080にアクセスする。

http://localhost:8080/

以下のようなログイン画面が表示されれば正常に起動したことになる。
f:id:nave_kazu:20150513010540p:plain

あとは初期ユーザroot(パスワードもroot)でログインして、ユーザ追加やグループの追加等の設定を行う。

その他

GitBucketのサイトにある通り、javaコマンドで起動する際にパラメータを与えることが出来る。

自分が真っ先に行ったのは「--gitbucket.home」の設定。
初期状態では実行ユーザのホームディレクトリにGitBucketの設定とリポジトリが作成される。

ホームディレクトリ以外をGitBucketの作業場所とするには、下記のように起動パラメータを与える。

java -jar gitbucket.war --gitbucket.home=D:\GitBucket

起動すると、D:\GitBucketに設定内容を保存したデータベースファイル(H2かな?)とリポジトリフォルダなどが作成される。

ここまでが初期設定。
さて、Git使ってみるぞ。

Spring Boot/第五回 Spring Bootで Web (一覧表示)

Spring BootでWebの画面を作ってみる。
まずは顧客一覧画面から。

Contrller

ブラウザからのリクエストを受け取ってビジネスロジック呼んでブラウザにレスポンスを返すのがコントローラの役目。
コントローラは以下のようになる。

package tools.springsample.springsample05.web;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import tools.springsample.springsample05.domain.Customer;
import tools.springsample.springsample05.service.CustomerService;

@Controller    // a
@RequestMapping("customer")    // b
public class CustomerContrller {
    @Autowired
    private CustomerService customerService;

    @RequestMapping(method=RequestMethod.GET)    // c
    public String list(Model model) {    // d
        List<Customer> customers = customerService.findAll();
        model.addAttribute("customers", customers);    // e
        return "customer/list";    // f
    }
}

a -> コントローラであることを記すアノテーション
b -> コントローラがリクエストを受け取るURLを「customer」と定義する。
c -> URLの「customer」にGETメソッドが送られたときに実行するメソッドであることを記すアノテーション
d -> 画面に値を渡すModelを引数に受け取る。
e -> ビジネスロジックの顧客一覧取得結果をモデルにセットする。
f -> 画面のテンプレート「customer/list.html」を呼び出す。

@Controllerアノテーションでコントローラであることを宣言して、@RequestMapping("customer")アノテーションで受け取るURLを定義して、@RequestMapping(method=RequestMethod.GET)アノテーションでHTTPメソッドの何を受け取って処理するメソッドかを宣言すると、そのメソッド(今回だとCustomerContrllerクラスのlistメソッド)が呼ばれる。
リクエスト処理(listメソッド)内では処理結果をモデルにセットして結果を表示するURLを戻り値で返せば、後のテンプレートへ処理結果を引き継げる。
今回は処理結果として、CustomerServiceで提供している顧客リスト取得メソッド(findAllメソッド)の結果をModelに「customers」という名前でセットした。

テンプレート

動的なページの作成に「Thymeleaf」(タイムリーフ)というテンプレートエンジンを使用する。
HTMLに「th:xxx」や「data-th:xxx」などのタグを入れるとThymeleafが適切な値に置き換えてくれる。

HTMLのテンプレートは下記のフォルダに置く。

src/main/resources/template

コントローラで「customer/list」と返しているので、対象のテンプレートは下記のファイルとして記述する。

src/main/resources/template/customer/list.html

まず普通のHTMLとして表示する画面を下記のようにする。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8"/>
  <title>顧客一覧</title>
  <style type="text/css">
    .list th {
      background-color: #cccccc;
      padding: 5px;
    }
    .list td {
      padding: 5px;
    }
  </style>
</head>
<body>
  <table class="list">
    <tr>
      <th>ID</th>
      <th>氏名</th>
      <th>アドレス</th>
    </tr>
    <tr>
      <td>xx</td>
      <td>name</td>
      <td>address</td>
    </tr>
  </table>
</body>
</html>

このHTMLをブラウザで開くと下記の通り。
f:id:nave_kazu:20150408010029j:plain

続いてHTMLにThymeleafで処理を書いて動的な結果を出力するように変える。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org/">    <!-- a -->
<head>
  <meta charset="UTF-8"/>
  <title>顧客一覧</title>
  <style type="text/css">
    .list th {
      background-color: #cccccc;
      padding: 5px;
    }
    .list td {
      padding: 5px;
    }
  </style>
</head>
<body>
  <table class="list">
    <tr>
      <th>ID</th>
      <th>氏名</th>
      <th>アドレス</th>
    </tr>
    <tr th:each="customer:${customers}">    <!-- b -->
      <td th:text="${customer.id}">xx</td>    <!-- c -->
      <td th:text="${customer.name}">name</td>    <!-- d -->
      <td th:text="${customer.email}">address</td>    <!-- e -->
    </tr>
  </table>
</body>
</html>

a -> Thymeleafの名前空間を宣言。
b -> コントローラでModelにセットしたリスト「customers」を取得して「th:each」で件数分ループさせる。
   ループ中の要素へアクセスするキーとして「customer」を指定する。
c -> 上で要素へのアクセスキー「customer」からIDを取得。
   customersはCustomerクラスのリストなので、Customerクラスのフィールドidのゲッターメソッド結果をTDタグの値として当てはめる。
d -> Customerクラスのフィールドnameのゲッターメソッド結果をTDタグの値として当てはめる。
e -> Customerクラスのフィールドemailのゲッターメソッド結果をTDタグの値として当てはめる。

Thymeleafの処理を追記したこのHTMLをブラウザで開くと下記の通り。
f:id:nave_kazu:20150408010113j:plain

追記前と変わらない。
つまりThymeleafの記述はブラウザには影響しない。
そのため、WebデザイナーとWebデベロッパーが分業して作業を進めることができる。
WebデザイナーがHTMLを書いて、そのあとWebデベロッパーがThymeleafの記述を追記する。
そのあとでもWebデザイナーはHTMLをブラウザで開いて表示内容を確認できるのでデザインを変更することもできる。

コンパイルして実行

以下のようにコンパイルする。

mvn package

続いて起動

mvn spring-boot:run

ブラウザで下記のアドレスにアクセスする。
http://localhost:8080/customer

するとデータベースの内容を読み込んで下記のように結果を表示する。
f:id:nave_kazu:20150408010152j:plain

まとめ

コントローラとテンプレートを作って動的な画面を表示してみた。
次回はフォームを使って更新系の画面を試してみる。

■ 参考文献 ■

この記事で参考にしたのは「はじめての Spring Boot」です。