Symfoware

Symfowareについての考察blog

SymfowareのデータをShunsakuと連携して検索を行う

SymfowareとShunsakuのドキュメントを読んでいると
「Symfowareに登録しているデータをエクスポート。
出力したデータをShunsakuにインポートすることで、対象テーブルの
全文検索が可能となります。」
というような内容を見つけました。

確かに、リレーショナル・データベースで全文検索を行おうとすると、
インデックスが効かない
LIKE '%検索対象%'
というようなクエリを発行しないといけません。

その点、Shunsakuは全文検索前提のシステムです。


試してみようと調べてみたのですが・・・
Interstage Data Collector
もしくは
Linkexpress Replication option
上記の製品が必要となる模様。

両方持っていないので、なんとか自力でこの機能が実現できないか
調べてみました。


対象のデータ



いつもの例で申し訳ないのですが、Symfowareに登録されている
郵便番号テーブルをShunsakuに移行してみようと思います。

データベース:sample
スキーマ:scm
テーブル:post_cd

保持している列は
郵便番号:char(7)
住所:nchar(50)
です。


Symfowareからのデータ抽出



住所をnchar(50)で宣言しているため、住所の後に全角の空白が入ります。
そのまま放置してもよかったのですが、せっかくなので抽出時に
空白を取り除くことにしました。

SQL文の実行結果をファイルに出力する(rdbunlsql)

ここで使用したテクニックを使用します。


rdbunlsql
-d SAMPLE
-s "SELECT 郵便番号,TRIM(TRAILING FROM 住所) FROM SCM.POST_CD"
-j SJIS
-t c:\export.txt



こんなバッチファイルを用意し、郵便番号と住所の列を持つexport.txtを得ました。



Shunsakuへのインポート用XMLに変換



Shunsakuにデータをインポートするには、XML形式に変換する必要があります。

作成するファイルのレイアウトです。

<post_cd>
    <zip_code>郵便番号1</zip_code>
    <address>住所1</address>
</post_cd>
<post_cd>
    <zip_code>郵便番号2</zip_code>
    <address>住所2</address>
</post_cd>
<post_cd>
    <zip_code>郵便番号3</zip_code>
    <address>住所3</address>
<post_cd>



全体を囲むタグは必要ありません。
それなので、厳密にはXML形式とはいえないかも知れません。

抽出したcsvファイルを上記xml形式に変換する必要がありますので、

コマンドプロンプトでテキストファイルを読み込み、文字を置換する
これを参考に変換用のバッチを作成しました。

※非常に面倒なので、何かしらのプログラム言語でxmlコンバートを
作成したほうが早いと思います。


@echo off

rem 遅延環境変数の展開を有効化
setlocal enabledelayedexpansion

for /f "delims=, tokens=1,2" %%I in (export.txt) do (
set zip_code=%%I
set address=%%J
echo ^<post_cd^>
echo ^<zip_code^>!zip_code:"=!^</zip_code^>
echo ^<address^>!address:"=!^</address^>
echo ^</post_cd^>
)

rem 遅延環境変数の展開を無効化
endlocal



ちなみに、^<と^>は、<と>のエスケープシーケンスです。

作成したファイルをconvert.batという名前で保存し、コマンドプロンプトから実行します。

C:\>convert.bat > import.xml




実行した結果、狙い通りのimport.xmlを得ました。


<post_cd>
<zip_code>0600000</zip_code>
<address>北海道札幌市中央区以下に掲載がない場合</address>
</post_cd>
<post_cd>
<zip_code>0640941</zip_code>
<address>北海道札幌市中央区旭ケ丘</address>
</post_cd>





Shunsakuへインポート実行



・現在登録されているデータを削除
・データ件数が0件となったか確認
・インポート実行
・データ件数が任意の値となったか確認
という手順で実行します。


データの削除コマンドは

shundclear -s [director識別子]



最初、director識別子がわからなかったのですが、標準インストールで
C:\Program Files\Interstage Shunsaku\Shunsaku\etc\director
ここに拡張子が.cfgのファイルがあると思います
この拡張子を除いた名称がdirector識別子にあたるようです。

標準インストールではshund1.cfgだと思いますので、

shundclear -s shund1


をコマンドプロンプトから実行してデータを削除します。



次に、データ件数の確認コマンドですが

shundstate -s [director識別子]


ですので、shundstate -s shund1 を実行してやります。

すると、こんな実行結果が得られます。

Time     State                DataFileStatus     DataSize             Fragment
s(%)
         LastBackedUp
         Records             ReadRecords         ReadTime(sec)        ReadSize


19:50:14 ACTIVE             NORMAL             0.00KB             0
         ----/--/-- --:--:--
         0                    7                    0.000                1.45KB


ACTIVEがデータ件数だろうと思います。
0になっているので、削除は成功しているようです。



いよいよ、データのインポートです。

shundimport
-s director識別子
-f [取り込み対象のxmlファイル]
-a
-n



-aはデータの追加指定。
-nはログの出力offオプションです。


以下のコマンドを実行したところで問題発生。

shundimport -s shund1 -f c:\import.xml -a -n



こんなエラーが発生しました。

shn21001i:Starting process... [shund1]
shn30355u:The data which is not well-formed XML document exists. [shund1]




調べてみると、どうやら一律エラーになるわけではなく、
たとえば、以下のデータはOK

<post_cd>
<zip_code>9071801</zip_code>
<address>沖縄県八重山郡与那国町与那国</address>
</post_cd>



このデータはNG

<post_cd>
<zip_code>0600035</zip_code>
<address>北海道札幌市中央区北五条東</address>
</post_cd>



差がさっぱりわからず、途方にくれていたのですが、

<post_cd>
<zip_code>0600035</zip_code>
<address>北海道札幌市中央区北五条東 </address>
</post_cd>


ちょっとわかりにくいですが、中央区北五条東と</address>の間に
全角スペースを入れてやるとインポートできることが判明。


以前、似たような現象に出くわしたことがあるので、ピンときたのですが、
ファイルがUTF-8で作成されている必要があるのでは?と思い、import.xmlを
メモ帳で開いて、文字コードにUTF-8を指定して保存しました。

再度、インポートコマンドを実行すると


shn21001i:Starting process... [shund1]
shn21002i:Processing has been completed. [shund1]



正常終了の模様。
shundstate -s shund1を実行すると、ACTIVEが122736と表示されました。
抽出した住所の件数と一致してますので、取り込みはうまく実行できたようです。



サンプルプログラム



地名に「銀座」が含まれる住所を検索するサンプルです。


import com.fujitsu.shun.ShunConnection;
import com.fujitsu.shun.ShunPreparedStatement;
import com.fujitsu.shun.ShunResultSet;

//結果取得
public class PostSelect {
    public static void main(String[] args) {
        ShunConnection con = null;
        ShunPreparedStatement pstmt = null;
        ShunResultSet rs = null;

        try {
            // 検索条件式
            String sQuery = "/post_cd/address = '銀座'";
            // リターン式
            String sReturn = "/post_cd/zip_code/text(), /post_cd/address/text()";
            // ヒット件数
            int iHitNum = 0;
            // データ件数のカウンタ
            int iDataCounter = 1;

            // ShunConnectionオブジェクトを作成
            con = new ShunConnection("192.168.1.250", 33101);

            // 検索式を指定し、ShunPreparedStatementオブジェクトを作成
            pstmt = con.prepareSearch(sQuery, sReturn);

            // 返信要求件数を設定
            pstmt.setRequest(1, 30);

            // 検索を実行し、ShunResultSetオブジェクトを作成
            rs = pstmt.executeSearch();

            // ヒット件数取得
            iHitNum = rs.getHitCount();
            System.out.println("ヒット件数     = " + iHitNum);
            // 検索条件に該当するデータを、1件ずつ取得
            while (rs.next()) {
                String tmp[][] = rs.getStringArray();
                
                System.out.println("[結果]" + iDataCounter + "件目 = "
                        + tmp[0][0] + ":" + tmp[1][0]);
                iDataCounter++;
            }

        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (Exception e) {
                }
            }
            if (pstmt != null) {
                try {
                    pstmt.close();
                } catch (Exception e) {
                }
            }
            if (con != null) {
                try {
                    con.close();
                } catch (Exception e) {
                }
            }
        }
    }

}



実行結果

ヒット件数     = 20
[結果]1件目 = 0691331:北海道夕張郡長沼町銀座
[結果]2件目 = 3220052:栃木県鹿沼市銀座
[結果]3件目 = 3600032:埼玉県熊谷市銀座
[結果]4件目 = 3670052:埼玉県本庄市銀座
[結果]5件目 = 1040061:東京都中央区銀座
[結果]6件目 = 9300991:富山県富山市新庄銀座
[結果]7件目 = 3940022:長野県岡谷市銀座
[結果]8件目 = 3940023:長野県岡谷市東銀座
[結果]9件目 = 3950031:長野県飯田市銀座
[結果]10件目 = 4240817:静岡県静岡市清水区銀座
[結果]11件目 = 4130013:静岡県熱海市銀座町
[結果]12件目 = 4140028:静岡県伊東市銀座元町
[結果]13件目 = 4750874:愛知県半田市銀座本町
[結果]14件目 = 4480845:愛知県刈谷市銀座
[結果]15件目 = 5220088:滋賀県彦根市銀座町
[結果]16件目 = 6128089:京都府京都市伏見区銀座町
[結果]17件目 = 7450032:山口県周南市銀座
[結果]18件目 = 7450033:山口県周南市みなみ銀座
[結果]19件目 = 7700916:徳島県徳島市銀座
[結果]20件目 = 8040076:福岡県北九州市戸畑区銀座





と、ここまでがんばったのですがSymfowareで

select * from scm.post_cd where 住所 LIKE '%銀座%'


というクエリを発行したときとそんなに速度差がないような・・・

12万件の単純なデータでは、あまり優位性が出せませんでした。
実運用上で存在する複雑なデータ構造じゃないと、本領発揮とは
いかないかもしれません。







テーマ:データベース - ジャンル:コンピュータ

  1. 2009/11/08(日) 12:31:36|
  2. Shunsaku
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Shunsaku を使用したアプリケーションの作成(Accesser.java)

Shunsaku を使用したアプリケーションの作成
この記事で紹介したサンプルアプリケーションのソースの一部です。

そのほかのソースは

Shunsaku を使用したアプリケーションの作成(MainFrame.java)
Shunsaku を使用したアプリケーションの作成(SubDialog.java)



package com.fc2.blog68.symfoware.shunsaku;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import com.fujitsu.shun.ShunConnection;
import com.fujitsu.shun.ShunPreparedKey;
import com.fujitsu.shun.ShunPreparedStatement;
import com.fujitsu.shun.ShunResultSet;

public class Accesser {

    //一覧検索
    public String[][] search(String query, String sort, String rtn) {
        ShunConnection con = null;
        ShunPreparedStatement pstmt = null;
        ShunResultSet rs = null;
        List<String[]> list = new ArrayList<String[]>();

        try {
            // ShunConnectionオブジェクトを作成
            con = createConnection();

            // 検索式を指定し、ShunPreparedStatementオブジェクトを作成
            pstmt = con.prepareSearch(query, rtn);

            // 返信要求件数を設定
            pstmt.setRequest(1, 30);

            // ソート式を指定
            pstmt.setSort(sort);

            // 検索を実行し、ShunResultSetオブジェクトを作成
            rs = pstmt.executeSearch();

            // 検索条件に該当するデータを、1件ずつ取得
            while (rs.next()) {
                String[][] tmp = rs.getStringArray();
                String[] row = new String[tmp.length];

                for (int i = 0; i < row.length; i++) {
                    row[i] = tmp[i][0];
                    for (int j = 1; j < tmp[i].length; j++) {
                        row[i] += "," + tmp[i][j];
                    }
                }

                list.add(row);
            }

        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            close(rs);
            close(pstmt);
            close(con);
        }

        String result [][] = null;
        if (0 < list.size()){
            result = new String[list.size()][];
            for (int i = 0; i < list.size(); i++) {
                result[i] = list.get(i);
            }
        }

        return result;
    }

    //単一検索
    public String[] search(String key, String rtn) {
        ShunConnection con = null;
        ShunPreparedKey preSkey = null;
        ShunResultSet rs = null;
        String[] result = null;

        try {

            // ShunConnectionオブジェクトを作成
            con = createConnection();
            preSkey = con.prepareSearchKey("key1", rtn);
            preSkey.add(key);
            preSkey.setSearchType(ShunPreparedKey.SHUN_KEY_COMPLETE_MATCH);
            rs = preSkey.searchByKey();

            while (rs.next()) {
                String[][] tmp = rs.getStringArray();
                result = new String[tmp.length];
                for (int i = 0; i < tmp.length; i++) {
                    result[i] = tmp[i][0];
                    for (int j = 1; j < tmp[i].length; j++) {
                        result[i] += "," + tmp[i][j];
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            close(rs);
            close(preSkey);
            close(con);
        }

        return result;
    }

    public String getMaxID() {
        // 検索条件式
        String query = "/doc/id !== 'DUMMY'";
        // リターン式
        String rtn = "max(/doc/id/text())";
        // ソート式
        String sort = "/doc/id/text() DESC";

        String[][] max = search(query, sort, rtn);

        if (max == null){
            return "0000000001";
        }

        String result = null;

        try {
            long maxID = Long.parseLong(max[0][0]);
            DecimalFormat numFormat = new DecimalFormat("0000000000");
            result = numFormat.format(maxID + 1);
        } catch (Exception e){

        }

        return result;
    }

    //新規登録
    public void insert(String xml) {
        ShunConnection con = null;
        ShunPreparedStatement pstmt = null;

        try {
            //ShunConnectionオブジェクト作成
            con = createConnection();

            //データ追加用にShunPreparedStatementオブジェクト作成
            pstmt = con.prepareInsert();

            // データを追加
            pstmt.add(xml);

            // データ追加を実行
            pstmt.executeInsert();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con);
            close(pstmt);
        }


    }

    //更新
    public void update(String key, String xml) {
        ShunConnection con = null;
        ShunPreparedKey preSkey = null;
        ShunResultSet rs = null;

        try {

            // ShunConnectionオブジェクトを作成
            con = createConnection();
            preSkey = con.prepareUpdateKey("key1");
            preSkey.add(key, xml);
            preSkey.updateByKey();

        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            close(rs);
            close(preSkey);
            close(con);
        }
    }

    //削除
    public void delete(String key) {
        ShunConnection con = null;
        ShunPreparedKey preSkey = null;
        ShunResultSet rs = null;

        try {

            // ShunConnectionオブジェクトを作成
            con = createConnection();
            preSkey = con.prepareDeleteKey("key1");
            preSkey.add(key);
            preSkey.deleteByKey();

        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            close(rs);
            close(preSkey);
            close(con);
        }
    }


    private ShunConnection createConnection() throws Exception {
        // ShunConnectionオブジェクトを作成
        return new ShunConnection("192.168.1.250", 33101);
    }

    private void close(Object obj) {
        if (obj == null ) {
            return;
        }

        try {
            if (obj instanceof ShunConnection) {
                ((ShunConnection)obj).close();
            } else if (obj instanceof ShunPreparedKey) {
                ((ShunPreparedKey)obj).close();
            } else if (obj instanceof ShunResultSet) {
                ((ShunResultSet)obj).close();
            } else if (obj instanceof ShunPreparedStatement) {
                ((ShunPreparedStatement)obj).close();
            } else {
                System.out.println("引数不正");
            }
        } catch (Exception e){
        }
    }

}








  1. 2009/10/08(木) 12:08:36|
  2. Shunsaku
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Shunsaku を使用したアプリケーションの作成(SubDialog.java)

Shunsaku を使用したアプリケーションの作成
この記事で紹介したサンプルアプリケーションのソースの一部です。

そのほかのソースは

Shunsaku を使用したアプリケーションの作成(MainFrame.java)
Shunsaku を使用したアプリケーションの作成(Accesser.java)



package com.fc2.blog68.symfoware.shunsaku;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class SubDialog extends JDialog implements ActionListener {

    private static final long serialVersionUID = 1L;
    private JTextField title;
    private JCheckBox chSyosetsu;
    private JCheckBox chManga;
    private JCheckBox chEhon;
    private JTextField publish;

    private String id;

    //新規登録
    public SubDialog() {
        super();
        this.setTitle("書籍追加");
        this.setModal(true);
        initDialog("new");
    }

    //編集
    public SubDialog(String id) {
        super();
        this.id = id;
        this.setTitle("書籍編集");
        this.setModal(true);
        initDialog("edit");

        Accesser db = new Accesser();

        StringBuffer rtn = new StringBuffer();
        rtn.append("/doc/title/text()");
        rtn.append(",/doc/category/text()");
        rtn.append(",/doc/pub_date/text()");

        String[] tmp = db.search(id, rtn.toString());

        title.setText(tmp[0]);

        if (tmp[1].indexOf("小説") != -1) {
            chSyosetsu.setSelected(true);
        }
        if (tmp[1].indexOf("漫画") != -1) {
            chManga.setSelected(true);
        }
        if (tmp[1].indexOf("絵本") != -1) {
            chEhon.setSelected(true);
        }

        publish.setText(tmp[2]);


    }

    private void initDialog(String cmd) {

        title = new JTextField(10);
        chSyosetsu = new JCheckBox("小説");
        chManga = new JCheckBox("漫画");
        chEhon = new JCheckBox("絵本");
        publish = new JTextField(10);
        JButton commit = new JButton();
        commit.setActionCommand(cmd);
        if ("new".equals(cmd)) {
            commit.setText("登録");
        } else if ("edit".equals(cmd)) {
            commit.setText("更新");
        }
        commit.addActionListener(this);

        getContentPane().setLayout(new GridLayout(4,1));

        JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
        p.add(new JLabel("タイトル"));
        p.add(title);
        getContentPane().add(p);

        p = new JPanel(new FlowLayout(FlowLayout.LEFT));
        p.add(new JLabel("カテゴリ"));
        p.add(chSyosetsu);
        p.add(chManga);
        p.add(chEhon);
        getContentPane().add(p);

        p = new JPanel(new FlowLayout(FlowLayout.LEFT));
        p.add(new JLabel("発行日"));
        p.add(publish);
        getContentPane().add(p);

        p = new JPanel(new FlowLayout(FlowLayout.CENTER));
        p.add(commit);
        getContentPane().add(p);

        pack();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        Accesser db = new Accesser();
        //登録
        if ("new".equals(cmd)) {
            id = db.getMaxID();
            if (id == null) {
                JOptionPane.showInternalMessageDialog(getContentPane(), "IDの取得に失敗");
                return;
            }
            db.insert(getData4XML());
        //更新
        } else if ("edit".equals(cmd)) {
            db.update(id, getData4XML());
        }

        this.dispose();

    }

    private String getData4XML() {
        StringBuffer xml = new StringBuffer();

        xml.append("<doc>");
        xml.append("<id>");
        xml.append(id);
        xml.append("</id>");
        xml.append("<title>");
        xml.append(title.getText());
        xml.append("</title>");

        if (chSyosetsu.isSelected()) {
            xml.append("<category>小説</category>");
        }
        if (chManga.isSelected()) {
            xml.append("<category>漫画</category>");
        }
        if (chEhon.isSelected()) {
            xml.append("<category>絵本</category>");
        }

        xml.append("<pub_date>");
        xml.append(publish.getText());
        xml.append("</pub_date>");
        xml.append("</doc>");

        return xml.toString();
    }


}






  1. 2009/10/08(木) 12:07:45|
  2. Shunsaku
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Shunsaku を使用したアプリケーションの作成(MainFrame.java)

Shunsaku を使用したアプリケーションの作成
この記事で紹介したサンプルアプリケーションのソースの一部です。

そのほかのソースは

Shunsaku を使用したアプリケーションの作成(SubDialog.java)
Shunsaku を使用したアプリケーションの作成(Accesser.java)



package com.fc2.blog68.symfoware.shunsaku;

import javax.swing.*;
import java.awt.FlowLayout;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.table.DefaultTableModel;

public class MainFrame extends JFrame implements ActionListener {


    private static final long serialVersionUID = 1L;
    private JTable table;

    private JTextField title;
    private JCheckBox chSyosetsu;
    private JCheckBox chManga;
    private JCheckBox chEhon;
    private JComboBox cbSort;

    public static void main(String[] args) {
        MainFrame frame = new MainFrame();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(10, 10, 500, 300);
        frame.setTitle("書籍管理");
        frame.setVisible(true);
    }

    private MainFrame() {

        JMenuBar menubar = new JMenuBar();
        JMenu fileMenu = new JMenu("操作");
        JMenuItem menuitem1 = new JMenuItem("新規作成");
        JMenuItem menuitem2 = new JMenuItem("編集");
        JMenuItem menuitem3 = new JMenuItem("削除");

        menuitem1.setActionCommand("new");
        menuitem1.addActionListener(this);

        menuitem2.setActionCommand("edit");
        menuitem2.addActionListener(this);

        menuitem3.setActionCommand("del");
        menuitem3.addActionListener(this);

        fileMenu.add(menuitem1);
        fileMenu.add(menuitem2);
        fileMenu.add(menuitem3);
        menubar.add(fileMenu);
        setJMenuBar(menubar);


        title = new JTextField(10);

        chSyosetsu = new JCheckBox("小説");
        chManga = new JCheckBox("漫画");
        chEhon = new JCheckBox("絵本");

        cbSort = new JComboBox(new String[]{"発効日昇順", "発効日降順"});


        JButton find = new JButton("検索");
        find.addActionListener(this);
        find.setActionCommand("find");
        JPanel p1 = new JPanel();
        p1.setLayout(new FlowLayout(FlowLayout.LEFT));
        p1.add(title);
        p1.add(chSyosetsu);
        p1.add(chManga);
        p1.add(chEhon);
        p1.add(cbSort);
        p1.add(find);

        DefaultTableModel model = new DefaultTableModel(null, new String[]{"ID","書籍名", "カテゴリー","発行日"});
        table = new JTable(model);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        //一行のみ選択可能。複数行選択を不可とする。
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        //セルの編集を禁止
        table.setDefaultEditor(Object.class, null);

        JScrollPane pane = new JScrollPane(table);

        getContentPane().add(p1, BorderLayout.PAGE_START);
        getContentPane().add(pane, BorderLayout.CENTER);
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        String cmd = e.getActionCommand();

        //検索
        if ("find".equals(cmd)){
            doFind();
        //新規
        } else if ("new".equals(cmd)){
            SubDialog d = new SubDialog();
            d.setVisible(true);
            doFind();
        //編集
        } else if ("edit".equals(cmd)){
            doEdit();
            doFind();
        //削除
        } else if ("del".equals(cmd)){
            doDelete();
            doFind();
        }
    }

    private void doFind() {
        Accesser db = new Accesser();

        StringBuffer query = new StringBuffer();
        StringBuffer sort = new StringBuffer();
        StringBuffer rtn = new StringBuffer();

        if (title.getText().length() != 0) {
            query.append("/doc/title = '" + title.getText() + "'");
        }

        //小説チェックあり
        if (chSyosetsu.isSelected()) {
            if (query.length() > 0) {
                query.append(" AND ");
            }
            query.append("/doc/category == '小説'");
        }
        //漫画チェックあり
        if (chManga.isSelected()) {
            if (query.length() > 0) {
                query.append(" AND ");
            }
            query.append("/doc/category == '漫画'");
        }
        //絵本チェックあり
        if (chEhon.isSelected()) {
            if (query.length() > 0) {
                query.append(" AND ");
            }
            query.append("/doc/category == '絵本'");
        }

        //ソートオーダー
        sort.append("/doc/pub_date/text()");
        if (cbSort.getSelectedIndex() == 1) {
            sort.append(" DESC");
        }

        //リターン
        rtn.append("/doc/id/text()");
        rtn.append(",/doc/title/text()");
        rtn.append(",/doc/category/text()");
        rtn.append(",/doc/pub_date/text()");

        //List<String[][]> result = db.search(query.toString(), sort.toString(), rtn.toString());
        String[][] result = db.search(query.toString(), sort.toString(), rtn.toString());

        ((DefaultTableModel)table.getModel()).setDataVector(result, new String[]{"ID","書籍名", "カテゴリー","発行日"});

    }

    //編集
    private void doEdit() {
        String id = getID();
        if (id == null) {
            return;
        }

        SubDialog d = new SubDialog(id);
        d.setVisible(true);
    }

    //削除
    private void doDelete() {
        String id = getID();
        if (id == null) {
            return;
        }

        Accesser db = new Accesser();
        db.delete(id);
    }

    private String getID() {
        //未選択時は-1
        int row = table.getSelectedRow();
        if (row == -1) {
            JOptionPane.showInternalMessageDialog(this.getContentPane(), "列を選択してください。");
            return null;
        }
        return table.getValueAt(row, 0).toString();
    }

}







  1. 2009/10/08(木) 12:06:54|
  2. Shunsaku
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Shunsaku を使用したアプリケーションの作成

ある程度Shunsakuの使い方がわかってきたので、
簡単な書籍の管理アプリケーションを作ってみます。

仕様は
・タイトル、ジャンル、発行日を管理
・ジャンルは複数保持可能
・タイトルとジャンルで検索
・発行日でソート条件を指定できる。
・データはダイレクトアクセス機能を使用して更新、削除

まあ、サンプルなのでこの程度で。。。

XMLのフォーマットはこんな感じ。


<doc>
<id>0000000001</id>
<title>書籍名</title>
<category>小説</category>
<pub_date>2006年08月</pub_date>
</doc>



categoryは複数保持可能とし、たとえば小説かつ漫画の
属性を持つ場合は、


<doc>
<id>0000000001</id>
<title>書籍名</title>
<category>小説</category>
<category>漫画</category>
<pub_date>2006年08月</pub_date>
</doc>



上記のようなフォーマットで保存することにします。


作成したサンプルアプリケーションの実行画面です。

■一覧画面
001_20090920200043.png

■追加画面
002_20090920200043.png

■更新画面
003_20090920200043.png


複数カテゴリに属する項目は、カンマ区切りでカテゴリを表示します。
004_20090920200043.png


検索実行後、rs.getStringArray()を実行すると、String[][]として
データが帰ってきます。
該当項目がひとつしかないデータの場合は、
result[i][0]
にデータが格納されていますが、今回の例で言うとカテゴリのように
複数のデータが返却された場合は、
result[i][0]、result[i][1]・・・result[i][n]
と、取得できた要素の数だけ配列が返却されます。


Shunsakuを触ってみた感想は

・認証が必要ない。
セキュリティ的に不安・・・ではなく、閉じたネットワークにあるシステムでは
認証機能が不要であることもあります。
認証機能を持たないことで、データベース本来の目的である検索に
特化したシステムだなーと感じました。

・簡単にスケールできる。
検索、ソートと機能ごとにサーバーを分割し、増設することが可能です。
データ数が増えた場合、チューニング以外に単純にサーバーを購入するという
選択肢があるのは利点かと。
通常のRDBだと、スケールさせるためには、アプリケーションの
改修が必要となる場合があるので、非常に強力な機能だと思います。


あんまり名前を聞かないデータベースですが、もっと流行っても
いいのかな?という感じです。


サンプルソースは、それなりの量になったので、別記事に分割して
掲載します。


Shunsaku を使用したアプリケーションの作成(MainFrame.java)
Shunsaku を使用したアプリケーションの作成(SubDialog.java)
Shunsaku を使用したアプリケーションの作成(Accesser.java)

  1. 2009/10/08(木) 12:05:18|
  2. Shunsaku
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
次のページ