• D言語によるWebアプリの実装 その4

    2011年10月28日 18時54分
    前回の続き

    今表示されているデータはテンプレートに直接書いているダミーです。
    これをデータベースから取得して表示します。


    今回のソース


    データベースからデータを取るためには、
    データベースをインストールし、データを入れておかなければなりません。

    以前と同様、環境構築もかんたんに記載します。

    Firebirdインストール

    データベースはFirebirdを使います。

    Firebirdに必要なものをインストール
    # yum -y install libstdc++-4.4.4-13.el6.i686
    # yum -y install ncurses-libs-5.7-3.20090208.el6.i686


    Firebird公式からSuperserverをダウンロードしてインストール
    # wget http://sourceforge.net/projects/firebird/files/firebird-linux-amd64/2.5.1-Release/FirebirdSS-2.5.1.26351-0.amd64.rpm/download
    # rpm -ivh FirebirdSS-2.5.1.26351-0.amd64.rpm



    アプリ用データベース準備


    Webアプリ用のユーザーを作成
    # /opt/firebird/bin/gsec -user sysdba -pass XXXXXX -add bbs -pw bbs


    データベースファイル置き場を作成
    # mkdir /usr/local/bbs/database
    # chown firebird:firebird /usr/local/bbs/database


    isqlを起動
    # /opt/firebird/bin/isql


    データベースを作成します
    > create database '/usr/local/bbs/database/bbs.fdb' user 'bbs' password 'bbs';


    データベースに接続
    > connect '/usr/local/bbs/database/bbs.fdb' user 'bbs' password 'bbs';


    テーブル定義を流し込みます。
    CREATE SEQUENCE thread_sequence;
    
    CREATE TABLE threads
    (
        id INTEGER NOT NULL PRIMARY KEY,
        title VARCHAR(1024) NOT NULL,
        registered TIMESTAMP NOT NULL
    );
    
    
    CREATE SEQUENCE response_sequence;
    
    CREATE TABLE responses
    (
        id INTEGER NOT NULL PRIMARY KEY,
        thread_id INTEGER NOT NULL,
        name VARCHAR(256) NOT NULL,
        content VARCHAR(10240) NOT NULL,
        registered TIMESTAMP NOT NULL
    );


    データ取得用に適当なデータも入れておきます。
    INSERT INTO threads(id, title, registered)
    VALUES (next value for thread_sequence, 'homu', CURRENT_TIMESTAMP);
    INSERT INTO responses(id, thread_id, name, content, registered)
    VALUES (next value for response_sequence, 1, 'homuhomu', 'homuhomuhomu', CURRENT_TIMESTAMP);



    アプリからデータを取得

    SQLの実行は次の手順で行います。

    1. データベースへ接続
    2. 以下必要回数繰し
      1. トランザクションを開始
      2. 以下必要回数繰し
        1. SQL実行準備(メモリ確保等)
      3. 以下必要回数繰し
        1. SQLに変数をセット(必要であれば)
        2. SQL実行
        3. SQLの戻り値を取得(必要であれば)
      4. 以下必要回数繰し
        1. SQL終了処理(メモリ解放等)
      5. トランザクションを終了(コミット or ロールバック)
    3. データベースから切断


    データベースの接続はコネクションプールを利用し、
    必要になった時に接続、アプリ終了時に切断という動作になります。

    コネクションプールの作成と終了処理はjp.ku6.bbs.mainで行っています。
    1ConnectionPool connectionPool = new ConnectionPool(DATABASE_PATH, DATABASE_USER, DATABASE_PASS, 10);
    2scope(exit) connectionPool.shutdown();



    トランザクションの制御はjp.ku6.bbs.logic.ThreadLogic.ThreadLogicクラスで行っています。
    この辺りのクラス構成はJavaのサーブレットでよく見る形に近いかと思います。

    コネクションはプールから取って使う形なので、
    必要になった時に取得し、トランザクションオブジェクトにセットします。
    1transaction_.connection = connectionPool_.lend(3);
    2scope(exit)
    3{
    4    connectionPool_.collect(transaction_.connection);
    5    transaction_.connection = null;
    6}



    コネクションをセットしたらトランザクションを開始します。
    また、終了時にコミット・例外発生時にロールバックとしています。
    1transaction_.begin();
    2scope(failure) transaction_.rollback();
    3scope(success) transaction_.commit();




    SQLの実行はjp.ku6.bbs.dao.ThreadDao.ThreadDaoクラスで行います。

    SQLの準備と終了処理は次の通りです。
    1statement.prepare();
    2scope(exit) statement.finish();


    今回は特に引数をセットしないのでそのまま実行します。

    1auto result = statement.execute();


    SQLがSELECT系の場合は値を取得します。
    1レコード分データを取得するには、fetchを使います。
    1while (result.fetch())


    カラムのデータを取得するには、インデックス番号とデータ型を使って
    次のように取得します。
    1thread.id = result.get!(uint)(0);



    データの表示

    SQLを使って取得したデータはjp.ku6.bbs.model.Thread.Threadクラスの
    配列として受け取ります。

    これをViewに渡し、表示処理を作成します。

    表示処理については前回同様、各項目用のdelegateを作成し、
    出力処理を書くだけですので、基本的にはソースを参考ください。


    一点だけ特殊な箇所を説明します。ForeachProcessorテンプレートです。
    1ForeachProcessor!(Thread) threads;


    ForeachProcessor!(T)は、配列を出力するための補助処理です。
    配列の個数だけ繰り返し出力処理を行うと同時に、
    現在処理中の値をForeachProcessor!(T).currentにセットします。

    実際のコードとは違いますが、次のような処理になっています。
    1void process(SweetsPrinter printer, SweetsElement element, const(SweetsNode[]) children)
    2{
    3    for (index_ = 0; index_ < array.length; ++index_)
    4    {
    5        element.printStartTag(printer, false);
    6        foreach (child; children)
    7            child.print(printer);
    8        element.printEndTag(printer);
    9    }
    10}


    ForeachProcessor!(T)をセットしたタグの
    子タグにセットされたdelegateは、このForeachProcessor!(T).currentにアクセスすることで、
    現在の値を参照することができます。

    いちいちタグ処理のdelegateを書くのは面倒極まりないので、
    このように共通化出来る処理は予め作成しておくと便利です。


    実行

    例によってテンプレートファイルと実行ファイルを更新します。
    うまく表示されない場合はデータベースの接続周りを確認してみてください。



    次回へ続く

    コメントを書く

    名前
    本文
    編集用パスワード 入力すると編集が行えます
    管理者のみ閲覧