nimbus (1.2.4) | 2018-01-25 20:02 |
nimbus-sample (1.2.4) | 2018-01-26 17:06 |
Java Beans規約に則ったBeanのプロパティ編集、及びJava Beans規約を拡張したBeanのプロパティアクセス、汎用DTOをサポートするライブラリを提供します。
関連するパッケージは、以下です。
Java Beans規約に則ったBeanのプロパティを編集する機能として、J2SEにはjava.beans.PropertyEditorインタフェースが定義されています。
しかしながら、その実装クラスは、公開されておらず、実装されているクラスもわずかです。そこで、Nimbusでは、java.beans.PropertyEditorインタフェースの実装クラスを拡充して提供します。
また、それらの実装クラスは、Nimbusのサービス定義や業務フロー定義のインジェクションで使用します。
Nimbusでサポートするプロパティ編集機能の一覧は、ここを参照して下さい。
以下に、簡単なサンプルコードを示します。
- import java.beans.PropertyEditor;
- import jp.ossc.nimbus.beans.NimbusPropertyEditorManager;
- // String配列のプロパティエディタを取得
- PropertyEditor editor = NimbusPropertyEditorManager.findEditor(java.lang.String[].class);
- // カンマ区切りのStringをString配列に変換する
- editor.setAsText("hoge,fuga,piyo");
- String[] array = (String[])editor.getValue();
- // String配列をカンマ区切りのStringに変換する
- editor.setValue(array);
- String str = editor.getAsText();
サンプルは、以下。
Java Beans規約を拡張したBeanのプロパティに、汎用的にアクセスする機能が、The Apache Jakarta Project のCommons BeanUtilsで提供されています。
BeanUtilsでは、プロパティにアクセスする際に、インタプリタ的にプロパティ文字列を解析して、リフレクションを利用してプロパティにアクセスします。
しかしながら、実際の用途としては、ある特定のプロパティに対して、プログラムが動作する度にアクセスするため、インタプリタ的に毎回プロパティ文字列の解析を行うのは無駄であるし、その後のリフレクションメソッドを毎回検索するのも無駄になってしまいます。
これは、staticに簡単に呼び出せる利便性を追求した一つの形ではありますが、エンタープライズシステムを構築するプログラマとしては、性能への悪影響を考えざる得ません。
そこで、Nimbusでは、プロパティ文字列の解析をプリコンパイル的に行い、更にリフレクションメソッドをキャッシュすることで、性能への影響を低減させたプロパティ汎用アクセス機能を提供します。
また、Java Beans規約の拡張という意味でも、BeanUtilsよりも拡張したアクセス機能を持っています。
さらに、オーバーロードメソッドの呼び出しや、publicなインタフェースを実装したpublicでないクラスのプロパティへのアクセスなど、細かな点が考慮されています。
Nimbusでサポートするプロパティアクセス機能の一覧は、ここを参照して下さい。
以下に、簡単なサンプルコードを示します。
- import jp.ossc.nimbus.beans.PropertyFactory;
- import jp.ossc.nimbus.beans.Property;
- class SampleBean{
- private String message;
- public void setMessage(String message){
- this.message = message;
- }
- public String getMessage(){
- return message;
- }
- }
- SampleBean bean = new SampleBean();
- // MessageプロパティにアクセスするPropertyを生成する
- Property property = PropertyFactory.createProperty("Message");
- // Messageプロパティに値を設定する
- property.setProperty(bean, "こんにちは");
- // Messageプロパティから値を取得する
- String message = (String)property.getProperty(bean);
サンプルは、以下。
DTO(Data Transfer Object)とは、アプリケーションがデータを受け渡しするためのデータ構造を表すオブジェクトである。
通常、アプリケーションで使用するデータ構造には、1次元で表現できるデータと、2次元で表現できるデータ、またその組み合わせで表現できるデータがあります。
Nimbusでは、このような汎用的なデータ構造を表現するクラスを、その時々に応じてクラスを実装して開発するコストを削減するために、データ構造を動的に定義して、汎用的なデータ構造を表現できる機能を提供します。
1次元構造を表すDTOは、Record
2次元構造を表すDTOは、RecordList
上記の組み合わせを表すDTOは、DataSet
です。
1次元構造を表すRecordは、データに対して型、制約を定義する事ができます。また、DTOは、交換を目的とするため、各データに対して入力時変換、出力時変換を定義する事もできます。
以下に、簡単なサンプルコードを示します。
- import jp.ossc.nimbus.beans.dataset.Record;
- // 1次元構造を定義する
- // プロパティ名prop1、型String、制約非null
- // プロパティ名prop2、型int、制約0より大きい、入力変換なし、出力変換カンマ編集
- // プロパティ名prop3、型Date、制約なし、入力変換"yyyyMMdd"の文字列からDateに変換、出力変換Dateから"yyyy/MM/dd"の文字列に変換
- Record record = new Record(
- ":prop1,java.lang.String,,,value != null\n"
- + ":prop2,int,,DecimalFormatConverter{ConvertType=NUMBER_TO_STRING;Format=#\\,###},value>0\n"
- + ":prop3,java.util.Date,DateFormatConverter{ConvertType=STRING_TO_DATE;Format=yyyyMMdd},DateFormatConverter{ConvertType=DATE_TO_STRING;Format=yyyy/MM/dd}"
- );
- // 各プロパティに値を設定する
- record.setProperty("prop1", "hoge");
- record.setProperty("prop2", 10000);
- record.setParseProperty("prop3", "20130820");
- // 各プロパティの値を取得する
- System.out.println(record.getProperty("prop1"));
- System.out.println(record.getProperty("prop2"));
- System.out.println(record.getFormatProperty("prop2"));
- System.out.println(record.getProperty("prop3"));
- System.out.println(record.getFormatProperty("prop3"));
- // レコードを検証する
- System.out.println(record.validate());
- // 各プロパティに異常値を設定する
- record.setProperty("prop1", null);
- record.setProperty("prop2", -1);
- // レコードを検証する
- System.out.println(record.validate());
- // 各プロパティを検証する
- System.out.println(record.validateProperty("prop1"));
- System.out.println(record.validateProperty("prop2"));
- System.out.println(record.validateProperty("prop3"));
2次元構造を表すRecordListは、プライマリキーやインデックスを定義し、検索する事ができます。また、ソートキーを指定して、昇順・降順ソートをする事もできます。
以下に、簡単なサンプルコードを示します。
- import jp.ossc.nimbus.beans.dataset.Record;
- import jp.ossc.nimbus.beans.dataset.RecordList;
- // 2次元構造を定義する
- // プロパティ名prop1、型String、制約非null、プライマリキー
- // プロパティ名prop2、型int、制約0より大きい、入力変換なし、出力変換カンマ編集
- // プロパティ名prop3、型Date、制約なし、入力変換"yyyyMMdd"の文字列からDateに変換、出力変換Dateから"yyyy/MM/dd"の文字列に変換
- // プロパティ名prop4、型boolean
- RecordList list = new RecordList(
- "List",
- ":prop1,java.lang.String,,,value != null,1\n"
- + ":prop2,int,,DecimalFormatConverter{ConvertType=NUMBER_TO_STRING;Format=#\\,###},value>0\n"
- + ":prop3,java.util.Date,DateFormatConverter{ConvertType=STRING_TO_DATE;Format=yyyyMMdd},DateFormatConverter{ConvertType=DATE_TO_STRING;Format=yyyy/MM/dd}\n"
- + ":prop4,boolean"
- );
- // キーインデックスを設定する
- // prop4の値でインデックスを作成する
- list.setIndex("INDEX_PROP4", new String[]{"prop4"});
- // レコードを作成する
- Record record1 = list .createRecord();
- record1.setProperty("prop1", "hoge");
- record1.setProperty("prop2", 10000);
- record1.setProperty("prop3", "20130820");
- record1.setProperty("prop4", true);
- list.add(record1);
- Record record2 = list .createRecord();
- record2.setProperty("prop1", "fuga");
- record2.setProperty("prop2", 500);
- record2.setProperty("prop3", "20130822");
- record2.setProperty("prop4", true);
- list.add(record2);
- Record record3 = list .createRecord();
- record3.setProperty("prop1", "piyo");
- record3.setProperty("prop2", 1000);
- record3.setProperty("prop3", null);
- record3.setProperty("prop4", false);
- list.add(record3);
- // 検索キーを生成する
- Record searchKey = list.createRecord();
- // プライマリキーで検索する
- searchKey.setProperty("prop1", "fuga");
- System.out.println("プライマリキーで検索。キー=" + searchKey);
- System.out.println(list.searchByPrimaryKey(searchKey));
- // キーインデックスで検索する
- searchKey.clear();
- searchKey.setProperty("prop4", true);
- System.out.println("キーインデックスで検索。キー=" + searchKey);
- List result = list.createView().searchByElement(searchKey, "INDEX_PROP4", null).getResultList();
- for(int i = 0; i < result.size(); i++){
- System.out.println(result.get(i));
- }
- System.out.println("現在のソート順");
- for(int i = 0; i < list .size(); i++){
- System.out.println(list .get(i));
- }
- // prop2の昇順にソートする
- list.sort(new String[]{"prop2"});
- System.out.println("ソート後");
- for(int i = 0; i < list .size(); i++){
- System.out.println(list .get(i));
- }
このDTOを利用する事で、DTOを開発するコストから開放される事に加え、変換や検証、検索、ソートといった、DTO周りのアプリケーションロジックの開発からも解放されます。
逆に欠点があるとすると、コンクリートなクラスでない事です。
IDEを使った開発では、Beanのプロパティに対するアクセサは、IDEが候補をあげてくれます。しかし、このDTOは、プロパティに対するアクセサは汎用であり、プロパティ名を引数でStringとして渡すAPIであるため、IDEにプロパティ名に対するヒントを与える事ができません。
また、コンパイラに対して、そのプロパティが存在するか、型は合っているのかを教える事もできないため、プロパティ名や型の整合性に対して、コンパイル時ではなくランタイム時にしかチェックする事ができません。
これらの欠点を解決する機能が、汎用DTOソース自動生成です。
この機能を使えば、汎用DTOを継承し、各プロパティのアクセサを持ったコンクリートなクラスを生成してくれます。どのようなクラスを生成するかは、データセット定義XMLファイルで定義します。
以下に、簡単なサンプル定義を示します。
- <dataSets>
- <!-- ヘッダ定義 -->
- <header code="sample.SampleHeader"
- name="SampleHeader">
- <schema>
- :prop1,java.lang.String,,,@value@ != null,1
- :prop2,int,,DecimalFormatConverter{ConvertType=NUMBER_TO_STRING;Format=#\,###},@value@>0
- :prop3,java.util.Date,DateFormatConverter{ConvertType=STRING_TO_DATE;Format=yyyyMMdd},DateFormatConverter{ConvertType=DATE_TO_STRING;Format=yyyy/MM/dd}
- :prop4,boolean
- </schema>
- </header>
- <!-- レコードリスト定義 -->
- <recordList code="sample.SampleRecordList"
- recordCode="sample.SampleRecord"
- name="SampleRecordList">
- <index name="INDEX_PROP4">prop4</index>
- </recordList>
- <!-- データセット定義 -->
- <dataSet code="sample.SampleDataSet"
- name="SampleDataSet">
- <header name="SampleHeader" code="sample.SampleHeader"/>
- <recordList name="SampleRecordList" code="sample.SampleRecordList"/>
- </dataSet>
- </dataSets>
サンプルは、以下。