|
|
iPhoneでYouTubeの再生が遅い場合の対処はこちらから。 |
2008-09-11
■[SAStruts][ExtJS][JSONIC] Full Ajax な CRUD アプリケーション その3

前回は画面のモックアップを作成しました。今回は実際にデータベースから取得したデータを表示するよう実装していきます。
データベースのセットアップ
まず最初はデータベースのセットアップを行います。データベースにはデフォルトでEMPテーブルとDEPTテーブルの二つが存在しています。これは必要ないので削除して下さい。
このサンプルで利用する社員テーブルのDDL文を以下に示します。
テーブル名EMPLOYEEのDDL文
create table EMPLOYEE ( ID integer generated by default as identity, NAME varchar(255), JOB_TYPE varchar(30), SALARY integer, DEPARTMENT varchar(255) )
DbLauncherを利用してH2データベースを立ち上げて上記DDL文を実行してください。その後は以下のテストデータを挿入してください。
テストデータ
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー01', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー02', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー03', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー04', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー05', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー06', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー07', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー08', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー09', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー10', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー11', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー12', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー13', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー14', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー15', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー16', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー17', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー18', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー19', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー20', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー21', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー22', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー23', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー24', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー25', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー26', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー27', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー28', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー29', '開発', 200000, '開発部');
insert into EMPLOYEE (NAME, JOB_TYPE, SALARY, DEPARTMENT) values ('テストユーザー30', '開発', 200000, '開発部');
S2JDBC-Genで各クラスの自動生成
ajax-app/s2jdbc-gen-build.xmlをAnt Buildで実行します。ビルドファイルを右クリックし「Run as」>「Ant Build...」を選択します。ターゲット一覧が表示されますのでその中から「gen-entity」のみを選択し実行します。実行が完了するとEmployee、EmployeeNames、EmployeeCondition、EmployeeService、AbstractService、EmployeeTestの五つ六つクラスが作成されます。
サーバーサイドのデータ一覧取得実装
EmployeeServiceにJSONデータを出力するメソッドfind()を作成します。ソースを以下に示します。
EmployeeService.java
package webapplication.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import webapplication.dto.PagerConditionDto;
import webapplication.entity.Employee;
import webapplication.entity.EmployeeNames;
/**
* {@link Employee}のサービスクラスです。
*
* @author tom
*/
public class EmployeeService extends AbstractService<Employee> implements EmployeeNames {
/**
* 識別子でエンティティを検索します。
*
* @param id
* 識別子
* @return エンティティ
*/
public Employee findById(Integer id) {
return select().id(id).getSingleResult();
}
/**
* JSONIC RESTモードで使用。エンティティ一覧を検索します。
*
* @param conditionDto
* ページング条件
* @return JSONの為のMapオブジェクト
*/
public Map<String, Object> find(PagerConditionDto conditionDto) {
long count =
jdbcManager
.from(Employee.class)
.getCount();
List<Employee> list =
jdbcManager
.from(Employee.class)
.orderBy(id)
.offset(conditionDto.start)
.limit(conditionDto.limit)
.getResultList();
Map<String, Object> m = new HashMap<String, Object>();
m.put("totalProperty", count);
m.put("root", list);
return m;
}
}
PagerConditionDto.java
package webapplication.dto;
public class PagerConditionDto {
public int start;
public int limit;
}
ブラウザからhttp://localhost:8080/ajax-app/employee/employee.jsonへアクセスしJSONデータが取得出来るのを確認して下さい。この機能はJSONICのサーブレットで実現しています。このサーブレットはSeasar2のコンポーネントを容易に扱う事が出来て、JSONへの変換も非常に簡単に出来る仕組みになっています。
グリッドのデータソースを実装
Ext.data.Storeを定義しサーバーサイドからデータを取得します。
storeのソース
var fields = [ 'id', 'name', 'jobType', 'salary', 'department' ];
var store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: './employee.json',
method: 'GET'
}),
reader: new Ext.data.JsonReader({
root: 'root',
totalProperty: 'totalProperty',
fields: fields
})
});
store.load({params:{start:0, limit:pageSize}});
employee.js - storeを定義
Ext.onReady(function(){
// MODEL, CONTROL
var fields = [ 'id', 'name', 'jobType', 'salary', 'department' ];
var store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: './employee.json',
method: 'GET'
}),
reader: new Ext.data.JsonReader({
root: 'root',
totalProperty: 'totalProperty',
fields: fields
})
});
// VIEW
var colModel = new Ext.grid.ColumnModel([
{header: "Id", width: 75, sortable: true, dataIndex: 'id'},
{id:'name', header: "氏名", width: 160, sortable: true, dataIndex: 'name'},
{header: "職種", width: 75, sortable: true, dataIndex: 'jobType'},
{header: "給与", width: 75, sortable: true, dataIndex: 'salary'},
{header: "部署", width: 85, sortable: true, dataIndex: 'department'}
]);
var addAction = new Ext.Action({
text: '追加',
iconCls: 'addIcon'
});
var editAction = new Ext.Action({
text: '編集',
iconCls: 'editIcon'
});
var deleteAction = new Ext.Action({
text: '削除',
iconCls: 'deleteIcon'
});
var findAction = new Ext.Action({
text: '検索',
iconCls: 'findIcon'
});
var tbar = [
addAction,
'-',
editAction,
deleteAction,
'-',
findAction
];
var pageSize = 20;
var bbar = new Ext.PagingToolbar({
id: 'pagingToolbar',
pageSize: pageSize,
store: store,
displayInfo: true,
displayMsg: '社員の一覧 {2} 件中 {0} - {1} 件目',
emptyMsg: "社員の一覧はありません"
});
var grid = new Ext.grid.GridPanel({
title:'社員管理',
stripeRows: true,
autoExpandColumn: 'name',
height:523,
width:600,
store: store,
colModel: colModel,
tbar: tbar,
bbar: bbar
});
// INIT
grid.render('grid-employee');
store.load({params:{start:0, limit:pageSize}});
});
ブラウザからhttp://localhost:8080/ajax-app/employee/index.htmlを開くとデータが読み込まれて表示されているのが確認できます。
今回は以上です。サーバーサイドの作成が簡単に出来るのをわかって頂けると思います。
次回はデータの追加、編集、削除機能を実装します。



ところで、EmployeeService.java内のimport webapplication.dto.PagerConditionDto;
dto.PageConditionDtoはどのように用意するものでしょうか?ヒントだけでもいただければ、ありがたいです。
おはようございます。ご回答ありがとうございます。
あと、EmployeeService.javaのinsert, update, deleteメソッドがあるバージョンも追加いただければ幸いです。
#最終回で検索機能を実装した完成版での掲載ということだったら、急かせて申し訳ありません。
サーバjson+クライアントjsの実装方法に大変興味がありましたので、今回の記事はとても有益です。
ありがとうございます。
まずs2jdbc-gen-build.xmlでgen-entityを実行すると五つのクラスが作成されると記述しましたが実際は六つです。AbstractService.javaが抜けていました。
EmployeeService.javaはAbstractService.javaを継承しています。さらにAbstractService.javaはs2containerのS2AbstractService.javaを継承しています。このS2AbstractService.javaでinsert, update, deleteメソッドが定義されています。EmployeeServiceはこのメソッドを呼び出して実行しています。
S2AbstractService.javaにはこの他にも良く使われるメソッドが定義されていますので一度ソースをご覧になるのをお勧めします。
ブログの記述は後で訂正追記しておきますね。
JSONICを見つけていなかったら私はこの記事を書いていなかったと思います。JSONICすばらしいですよね。
ご教示ありがとうございます。
AbstractService.javaはチェックしたのですが、その先まではチェックしていませんでした。
アドバイスいただきましたとおり、S2AbstractService.javaのソースをチェックします。
JSONICもS2もすごいのですが、今回の記事のように、実際の使い方の説明のような情報が不足しているように思います。
#S2もPetStoreのようなサンプル実装を用意すればいいのに、と思います。
今回のUchima様の記事はまさに自分にとっては待ち望んでいたものです。次回で最終回とおっしゃらず、続けていただくことを希望します。
EmployeeService.java
に
public class EmployeeService extends AbstractService<Employee> implements EmployeeNames {
とあるのですが、
eclipseで以下のメッセージが出てしまいます。
型 EmployeeNames は EmployeeService のスーパーインターフェースにできません。スーパーインターフェースはインターフェースでなければなりません
また
public Map<String, Object> find(PagerConditionDto conditionDto) {
の
.orderBy(id)
についても、eclipseで以下のメッセージが出てしまいます。
id を解決できません。
結果的に
http://localhost:8080/ajax-app/employee/employee.json
にアクセスしても
404エラーとなってしまいます。
大変お手数ですが、解決法を教えていただけましたら幸いです。
EmployeeService 、 EmployeeNames はS2JDBC-Genを用いて自動生成されるクラスです。何かの拍子でEmployeeNamesに問題が発生しているように伺えます。EmployeeService 、 EmployeeNames 、 Employee クラスを削除し再作成してみて下さい。
ページング処理の部分で質問がございます。
下記で対象レコードを抽出する方法は分かったのですが、
.offset(conditionDto.start)
.limit(conditionDto.limit)
そもそも、ExtJS側から送られてくるURLパラメータのstartとlimitの値は、
サーバサイドで具体的にどのようにして受け取っているのでしょうか?
以上、ご回答よろしくお願いします。
この記事の中程にも書きましたが一覧表示やページング機能はJSONICのRESTサーブレットで実現しています。具体的にどのように受け取っているかは以下のドキュメントを読めば理解出きるかと思います。
http://jsonic.sourceforge.jp/webservice.html#restservlet
このプログラムの例ですとEmployeeServiceがResourceServiceにあたります。そのクラスにfindメソッドを定義し、その引数でPagerConditionDto型のオブジェクトを定義しています。
PagerConditionDtoではstartとlimitを定義しており、JSONICのRESTサーブレットではプロパティ名とURLパラメータの名称が一致していれば値が自動的に入るようになっています。
もっと具体的に知りたければRESTServletのソースを読んでみてください。