JAXSON 業務アプリケーションフレームワーク
トップ

インストール

ドキュメント
チュートリアル
開発ガイド
トランザクション管理
データベース接続
データベースアクセス
バリデーション
セキュリティ
エクセルファイル出力
JavaScriptリファレンス
API doc

ダウンロード
jaxson-2.1.zip

チュートリアル

テーブルの検索と登録を行うアプリケーションを作成します。
対象となるテーブルは下記のような構造となっています。

カラム名 属性
customer_idINT(8)プライマリキー、auto_increment
nameVARCHAR(64)NOT NULL
addressVARCHAR(128)NOT NULL
phoneVARCHAR(16)NOT NULL
emailVARCHAR(64)NULL

検索アクション
検索条件を入力するとテーブルに結果を表示する画面を作成します。

最初にアクションクラスを作成します。
public class CustomerSearch extends ServiceAction {
    public Object execute() throws Exception {
        return null;
    }
} 
アクションクラスは jp.simm.jaxson.service.ServiceAction を継承します。 処理を実行してその結果を返すexecute()は必ず実装する必要があります。

処理結果の出力形式をResponder注釈によって指定します。
@Responder(JsonResponder.class)
public class CustomerSearch extends ServiceAction {
    ... 
Responder注釈を省略した場合はJsonResponderがデフォルトとして使用されますので この場合は注釈の必要はありません。

クライアントからパラメータが渡される場合は その受け皿となるフィールドを作成します。
public class CustomerSearch extends ServiceAction {

    @ContextParam
    private String name;

    @ContextParam
    private String address;

    @ContextParam
    private String phone;

    @ContextParam
    private String email;
    
    ... 
ContextParam注釈されたフィールドはアクション生成時に指定したスコープから フィールド名称と同じ名前のパラメータ値がセットされます。
スコープはリクエスト、セッション、サーブレットパラメータ、アプリケーション設定から 指定することができますが、ここではデフォルトであるリクエストから取得するためにスコープを指定していません。

次にデータベースアクセスを行うPersisterフィールドを作成します。
public class CustomerSearch extends ServiceAction {

    @Connector("default")
    @TableDEF("customer");
    BasicPersister persister;
    
    ... 
Persisterフィールドはアクション生成時にインスタンスが生成されてデータベース接続がセットされます。
接続先はConnector注釈の接続先名によって指定します。 接続先名が "default" の接続先がデフォルトですのでこれも実際には必要ありません。
Persisterには1レコードを表現するクラスとしてMapを使用するBasicPersisterと Beanを使用するGenericPersisterの2種類があります。 ここで使用しているBasicPersisterはTableDEF注釈で対象となるテーブルを指定します。 TableDEF注釈で複数のテーブルを指定したり、またその結合条件を記述することができます。

必要な材料が揃いましたので実行メソッドを実装します。
public Object execute() throws Exception {
    SelectOption option = new SelectOption()
        .addWhere(new Like("name", Like.CONTAINS, name),
            new Like("phone", Like.CONTAINS, phone),
            new Like("address", Like.CONTAINS, address),
            new Like("email", Like.CONTAINS, email))
        .addOrder("customer_id");
    return persister.select(option);
} 
クライアントから渡されたパラメータを部分一致条件として検索を行っているのが分かると思います。
検索条件をJavaオブジェクトとして指定するのは面倒に思うかもしれませんが、 これにより状況によって生成するSQLを動的に変更することが可能になります。 これだけで「値が空もしくはNULLのものは条件として出力しない」 「検索条件が全て空の場合は検索を行わない」という機能を実現しています。
BasicPersister#select(SelectOption)メソッドは 1レコードの情報を格納したMapオブジェクトのListを返しますので、 クライアントはテーブルのカラム名を属性名とするJavaScriptオブジェクトの配列として受け取ります。

一覧検索画面
画面はHTMLで記述します。 DOMを使用しますのでXHTMLに準拠する必要がありますが、 タグ名を小文字にする、必ず終了タグを書くということに気をつければ問題は起こらないと思います。
<body>
  名前 : <input type="text" id="nameCondition" /><br/>
  住所 : <input type="text" id="addressCondition" /><br/>
  電話 : <input type="text" id="phoneCondition" /><br/>
  メール : <input type="text" id="emailCondition" /><br/>
  <table id="customers">
    <thead>
      <tr>
        <td id="name">名前</td>
        <td id="address">住所</td>
        <td id="phone">電話番号</td>
        <td id="email">メールアドレス</td>
      </tr>
    </thead>
  </table>
</body> 
要素に "id" を指定することでJavaScriptで参照する時にその要素を特定することができます。 idはドキュメント内で一意でなくてはならないことに注意してください。
検索結果を読込むテーブルのヘッダセルにidを設定しておくと オブジェクトを読込む際にそのidと同じ属性名の値がその列に読込まれます。 検索アクションがカラム名をキートするMapのListを出力しますから、 このidにテーブルカラム名を指定しておきます。

このページをブラウザに表示するとこのようになります。
  名前 : 
住所 :
電話 :
メール :
名前 住所 電話番号 メールアドレス

一覧検索JavaScript
サーバのアクションを呼出し、検索結果をテーブルに表示するJavaScriptを作成します。 JavaScriptはHTMLに埋込まずに別ファイルとした方が良いでしょう。

HTMLファイルにJavaScriptをインポートします。
<head>
    ...
    <script type="text/javascript" src="/sample/js/prototype/prototype.js"></script>
    <script type="text/javascript" src="/sample/js/jaxson/jaxson.js"></script>
    <script type="text/javascript" src="/sample/js/common.js"></script>
    <script type="text/javascript" src="/sample/js/customerSearch.js"></script>
    ...
</head> 
JavaScriptファイルは必ず上記の順でインポートしなくてはなりません。

customerSearch.jsファイルを作成します。
function init() {
    $('nameCondition').onkeyup = searchCustomer;
    $('addressCondition').onkeyup = searchCustomer;
    $('phoneCondition').onkeyup = searchCustomer;
    $('emailCondition').onkeyup = searchCustomer;
}

function searchCustomer() {
    var params = new Object();
    params.name = $F('nameCondition');
    params.address = $F('addressCondition');
    params.phone = $F('phoneCondition');
    params.email = $F('emailCondition');
    doAction('jp.simm.sample.action.CustomerSearch', params, function(result) {
        if (result) {
            setElementValue($('customers'), result);
        }
    });
} 
initはドキュメントロード時に実行する定義済み関数でDOM要素が全て読込まれた後に呼び出されますので、 初期化処理でDOM要素を参照する必要がある処理はこの関数内に記述します。
"$(xx)"は prototype.js が提供する関数で、括弧内で指定した値をidとする要素を返します。 この初期処理内で検索条件の各フィールドに入力が合った場合に searchCustomer という関数を呼び出すようにしています。
検索アクションを呼び出す searchCustomer 関数ではまずアクションに渡すパラメータオブジェクトを作成します。 "$F(xx)"は xx の値をidとして持つ要素の値を返す prototype.js の関数で、 "$(xx).value" と同じです。
このパラメータオブジェクトにセットしている属性の名称がアクションクラスのフィールド名と対応しています。
アクションを呼び出す "doAction" 関数は、アクション名、パラメータ、アクションの処理結果を引数とする関数を引数として Ajaxによる非同期呼出しを行います。
この例のようにアクション名にアクションクラスのフル修飾名を指定することによって 設定を行うことなくアクションを呼び出す事ができますが、 設定ファイルにパッケージを登録することによりパッケージなしのクラス名で呼び出す事もできます。
アクションの処理結果は "setElementValue" 関数によりテーブルに読込まれます。
  名前 : 
住所 :
電話 :
メール :
名前 住所 電話番号 メールアドレス
望月誠二 長野県佐久市望月 0267-55-5555 mochi@simm.jp

登録アクション
customerテーブルの登録・更新を行うアクションを作成します。
public class CustomerEdit extends ServiceAction {

    @ContextParam
    private Customer customer;
    
    @Entity(Customer.class)
    private GenericPersister persister;
    
    public Object execute() throws Exception {
        int updateCount = 0;
        if (customer.getCustomer_id() != null) {
            updateCount = persister.update(customer);
        } else {
            updateCount = persister.insert(customer);
        }
        return updateCount;
    }
} 
今回はクライアントから渡されるパラメータの受け皿のフィールドに BeanクラスであるCustomerクラスを使用します。
ContextParam注釈されたフィールドに値をセットする場合、 intなどのプリミティブ型およびそのラッパークラス、 String、Date、Timestamp、BigDecimalなどの基本型であればそのクラスのオブジェクトに変換します。
変換ができない場合はそのフィールドの型がBeanであると仮定して そのクラスの各フィールドに対してパラメータをセットしようと試みます。
渡されるパラメータの数が多い場合はBeanクラスをパラメータフィールドにするとアクションクラスをシンプルに保つことができます。 データベーステーブルの1レコードを表現するBeanクラスを特にエンティティクラスと呼びますが、 このエンティティクラスの実装は非常に退屈で面倒ですので JAXSONにはデータベースと接続してこのエンティティクラスを作成するユーティリティを用意しています。
エンティティクラス作成ツールを使用して作成したCustomerクラスを以下に示します。
@TableDEF("customer")
public class Customer implements Serializable {

    @FieldDEF (
        table = "customer",
        column = "customer_id",
        validator = RequiredValidator.class
        key = true
    )
    private Integer customer_id;

    @FieldDEF (
        table = "customer",
        column = "name",
        validator = RequiredValidator.class
    )
    private String name;

    @FieldDEF (
        table = "customer",
        column = "address",
        validator = RequiredValidator.class
    )
    private String address;

    @FieldDEF (
        table = "customer",
        column = "phone",
        validator = RequiredValidator.class
    )
    private String phone;

    @FieldDEF (
        table = "customer",
        column = "email",
    )
    private String email;

    public Integer getCustomer_id() {
        return customer_id;
    }
    ...
}            
クラスに注釈されたTableDEFで対象となるテーブルを指定します。
各フィールドに注釈されたFieldDEFによってテーブル名、カラム名が特定される他、 validatorで入力チェックを指定できます。
エンティティ作成ツールで「必須チェック」にチェックして作成したために NOT NULL のフィールドに "RequiredValidator" が追加されています。
プライマリキーである customer_id は auto_increment 属性がセットされているため、 新規登録の場合に顧客IDを指定する必要がないため、customer_id の必須チェックは削除しておきます。
ここではメールアドレスのチェックも追加しておきます。
@FieldDEF (
    table = "customer",
    column = "email",
    validator = GenericFieldValidator.class,
    validatorParams = "isEmail",
    validatorMessage = "メールアドレスに誤りがあります。"
)
private String email; 

登録画面
各入力フィールドの id をエンティティクラスのフィールド名に合わせておきます。
<body>
  <form id="customerForm">
    名前 : <input type="text" id="name" /><br/>
    住所 : <input type="text" id="address" /><br/>
    電話 : <input type="text" id="phone" /><br/>
    メール : <input type="text" id="email" /><br/>
    <input type="hidden" id="customer_id" />
  </form>
  <br/>
  <input id="update" type="button" value="登録" onclick="updateCustomer()" />
</body> 

登録JavaScript
検索ではサーバに渡すパラメータをひとつひとつオブジェクトにセットしましたが、 パラメータが多くなるとこの方法では非常に面倒になります。 ここではjaxson.jsで用意している FORMの子要素をオブジェクトに格納する formToObject 関数を使います。
function updateCustomer() {
    doAction('jp.simm.sample.action.CustomerEdit', formToObject($('customerForm')),
        function(data) {
            if (data && data.value == 1) {
                alert("登録が完了しました。");
            } else {
                alert("登録に失敗しました。");
            }
        }
    );
} 
Persister.update(E) および Persister.insert(E) は更新レコード数を返しますから、 それによって登録の成功、失敗を判断してダイアログに表示しています。
JSONとして表現できるのは配列もしくはキーと値の対を持つオブジェクトに限られるため、 JSONに変換できない単純な数値、文字列などは "value"というフィールドを持つオブジェクトに格納します。 この場合は上記のように "value" という属性名で値を取得します。