Seasar DI Container with AOP

S2Dxo

目次

S2Dxo とは

DXO (Data eXchange Object) とは,JavaBeansとJavaBeansまたはJavaBeansとMapを相互変換 (exchange) することを目的としたオブジェクトです.主な用途としては,プレゼンテーション層のモデル (ページ) とドメイン層のモデル (エンティティ) を相互変換することを意図しています.

S2DxoはDXOを実現するためのフレームワークです.S2Dxoを使うと,Javaインタフェースを定義するだけでDXOを実現することが可能となります.

クイックスタート

さっそく試してみましょう.登場人物は次のようになります.

変換元のJavaBeans
EmployeeとDepartmentの二つ.EmployeeはDepartmentを参照します.
変換先のJavaBeans
EmployeePage.EmployeePageはEmployeeとDepartmentの両方のプロパティを持っています.
変換を行うDXO
EmployeeDxo.EmployeeとDepartmentをEmployeePageに変換するDXOのJavaインタフェースです.

S2Dxoは,上記3つのJavaBeansに対して,以下の図のような変換を行います.

Employee.java

変換元のJavaBeansです.Departmentを参照します.

package examples.dxo;

public class Employee {
    private String ename;
    private Department department;

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }
}
Department.java

変換元のJavaBeansです.Employeeから参照されます.

package examples.dxo;

public class Department {
    private String dname;

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }
}
EmployeePage.java

変換先のJavaBeansです.Employeeのプロパティenameに加えて,Departmentのプロパティdnameも持っています.toString()メソッドではこの二つのプロパティを文字列化しています.

package examples.dxo;

public class EmployeePage {
    private String ename;
    private String dname;

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public String toString() {
        return "employee : " + ename + ", department : " + dname;
    }
}
EmployeeDxo.java

変換を行うDXOです.引数として変換元のJavaBeansであるEmployeeを受け取り,戻り値として変換先のJavaBeansであるEmployeePageを返します.単なるJavaインタフェースで実装がないことに注目してください.

package examples.dxo;

public interface EmployeeDxo {
    EmployeePage convert(Employee employee);
}
app.dicon

EmployeeDxoを利用するために必要な設定を記述します.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="dxo.dicon"/>

        <component class="examples.dxo.EmployeeDxo">
                <aspect>dxo.interceptor</aspect>
        </component>
</components>
DxoMain.java

サンプルを実行するためのクラスです.S2コンテナを初期化した後,変換元のJavaBeansを準備しています.それをS2コンテナから取得したDXOを使って変換しています.

package examples.dxo;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

public class DxoMain {

    public static void main(String[] args) {
        SingletonS2ContainerFactory.setConfigPath("examples/dxo/app.dicon");
        SingletonS2ContainerFactory.init();
        S2Container container = SingletonS2ContainerFactory.getContainer();

        Employee employee = new Employee();
        employee.setEname("Mike");
        Department department = new Department();
        department.setDname("Sales");
        employee.setDepartment(department);

        EmployeeDxo dxo = (EmployeeDxo) container
                .getComponent(EmployeeDxo.class);
        EmployeePage page = dxo.convert(employee);
        System.out.println(page);
    }
}
実行結果

DxoMainの実行結果は以下のようになります.EmployeeとDepartmentのプロパティがEmployeePageに設定されたことが分かります.

employee : Mike, department : Sales

このサンプルはSeasar2.4配布ファイルのexamplesに含まれています.

DXOの定義

インタフェース/クラス定義

DXOは通常Javaインタフェースとして定義します.抽象クラスとして定義することもできますが,S2Dxoが適用されるのは抽象メソッドのみとなります.

次のようになります.

publi interface FooDxo {
    ・・・
}

SMART deployを使用する場合,DXOのパッケージ名およびクラス名はSMART deployの規約に従ってください.SMART deployについては「Seasr2.4の新機能-SMART deploy」を参照してください.

メソッド定義

DXOには変換を行うメソッドを定義します.メソッド名は自由ですが,慣例としてconvertで始めます.

DXOのメソッドは次の2つの形式のいずれかにすることができます.

  • 変換先型 メソッド名(変換元型)
  • void メソッド名(変換元型, 変換先型)

戻り値が変換後型の場合はS2Dxoが変換後のインスタンスを生成して返します.戻り値型がvoidの場合は第2引数で受け取ったインスタンスを更新します.

変換前及び変換後の型の組み合わせは次のものがサポートされています.

変換前型 変換後型
JavaBeans JavaBeans
JavaBeans[] JavaBeans[]
JavaBeans java.util.Map
JavaBeans[] java.util.Map[]
java.util.Map JavaBeans
java.util.Map[] JavaBeans[]

例えば次のようになります.

public interface FooDxo {
    ToBean convertBeanToBean(FromBean fromBean);
    void convertBeanToBean(FromBean[] fromBeans, ToBean[] toBeans);

    Map convertBeanToMap(FromBean fromBean);
    void convertBeanToMap(FromBean[] fromBeans, Map[] toMaps);

    ToBean convertMapToBean(Map fromMap);
    void convertMapToBean(Map[] fromMaps, ToBean[] toBeans);
}

Java5のGenericsを使うとJavaBeansまたはMapのListもサポートされます.

変換前型 変換後型
JavaBeans[] java.util.List<JavaBeans>
java.util.List<JavaBeans> JavaBeans[]
java.util.List<JavaBeans> java.util.List<JavaBeans>
JavaBeans[] java.util.List<java.util.Map>
java.util.List<JavaBeans> java.util.Map[]
java.util.List<JavaBeans> java.util.List<java.util.Map>
java.util.Map[] java.util.List<JavaBeans>
java.util.List<java.util.Map> JavaBeans[]
java.util.List<java.util.Map> java.util.List<JavaBeans>

例えば次のようになります.

public interface BarDxo {
    List<ToBean> convert(FromBean[] fromBeans);
    Map[] convert(List<FromBean> fromBeans);
    List<ToBean> convert(List<Map> fromMaps);
}

Mapの型引数を指定することもできます.キーの型はStringまたはStringを代入可能な型のみ有効です.値の型は任意の型を指定することができます.変換元の値が型引数で指定された型に代入できない場合は型変換が行われます.

例えば次のようになります.

public interface BarDxo {
    Map<String, String> convert(FromBean fromBean);
    List<Map<String, String> convert(FromBean[] fromBeans);
}

インターセプタの適用

DXOインタフェース/クラスにはS2Dxoの提供するインターセプタを適用します.

<include path="dxo.dicon"/>

<component class="examples.dxo.FooDxo">
    <aspect>dxo.interceptor</aspect>
</component>

SMART deployを使用する場合は,app.dicondxo.diconをインクルードし,customizer.diconに記述されているdxoCustomizerの設定を次のようにします.

<component name="dxoCustomizer" class="org.seasar.framework.container.customizer.CustomizerChain">
    <initMethod name="addCustomizer">
        <arg>defaultCustomizer</arg>
    </initMethod>
    <initMethod name="addCustomizer">
        <arg>
            <component class="org.seasar.framework.container.customizer.AspectCustomizer">
                <property name="interceptorName">"dxo.interceptor"</property>
            </component>
        </arg>
    </initMethod>
</component>

Customizerの詳細は「Seasr2.4の新機能-SMART deploy」を参照してください.

変換ルール

JavaBeansのプロパティとpublicフィールド

Seasar2は,2.4.17からpublicフィールドをプロパティとして扱う機能をサポートしています.S2Dxoもpublicフィールドをサポートします.本ドキュメントにおける「プロパティ」は,JavaBeans本来のgetter/setterメソッドによるプロパティに加えて,publicフィールドも含みます.

変換の基本

JavaBeansまたはMapからJavaBeansに変換する場合,変換先となるJavaBeansのプロパティを変換元から探し,(必要なら)型変換を行って変換先のプロパティに設定します.

JavaBeansからMapに変換する場合は,変換元のJavaBeansの全プロパティについて変換先のMapに追加します.追加されるエントリのキーは変換元のプロパティ名(String),値は変換元のプロパティ値です.

型変換

変換元と変換先の型が異なる場合は型変換が行われます.S2Dxoが標準で提供する型変換は以下の通りです.変換先の型を基準に参照してください.

変換元の型 変換先の型 説明
boolean/Boolean boolean/Boolean -
Number 数値が1より大きければtrue,そうでなければfalseに変換します.
その他任意の型 変換元を文字列化し,それが"yes""y""true""on""1"のいずれかであればtrue,そうでなければfalseに変換します.
boolean/Boolean byte/Byte
short/Short
int/Integer
long/Long
float/Float
double/Double
java.math.BigDecimal
java.math.BigInteger
変換元がtrueなら1,そうでなければ0に変換します.
Number 同等の値に変換します.
プリミティブ型への変換は,Number#xxxValue()で行われます.
BigDecimal/BigIntegerへの変換は,変換元を文字列化したものをコンストラクタ引数に渡すことで行われます.
String 文字列の表現する数値と同等の値に変換します.
Enumのサブクラス 列挙定数の序数に変換します (S2-Tigerが必要です).
序数への変換はEnum#ordinal()で行われます.
char/Character char/Character -
String String -
char[] 変換元を文字シーケンスとして持つStringに変換します.
Enumのサブクラス 列挙定数の名前に変換します (S2-Tigerが必要です).
列挙定数への変換はEnum#name()で行われます.
java.util.Calender
java.util.Date
java.sql.Date
DATE_PATTERNフォーマットに従い変換します.
詳細は「日付・時刻のフォーマット」を参照してください.
java.sql.Time TIME_PATTERNフォーマットに従い変換します.
詳細は「日付・時刻のフォーマット」を参照してください.
java.sql.Timestamp TIMESTAMP_PATTERNフォーマットに従い変換します.
詳細は「日付・時刻のフォーマット」を参照してください.
その他任意の型 変換元の文字列表現の持つ文字配列に変換します.
char[] char[] -
String 変換元の持つ文字配列に変換します.
その他任意の型 変換元の文字列表現に変換します.
java.util.Calendar java.util.Calendar -
java.util.Date 変換元の時刻値を持つjava.util.Calendarに変換します.
Number 変換元のlong値を時刻値として持つjava.util.Calendarに変換します.
String DATE_PATTERNフォーマットに従いjava.util.Calendarに変換します.
詳細は「日付・時刻のフォーマット」を参照してください.
java.util.Date java.util.Date -
java.util.Calendar 変換元の時刻値を持つjava.util.Dateに変換します.
Number 変換元のlong値を時刻値として持つjava.util.Dateに変換します.
String DATE_PATTERNフォーマットに従いjava.util.Dateに変換します.
詳細は「日付・時刻のフォーマット」を参照してください.
java.sql.Date java.sql.Date -
java.util.Date 変換元の時刻値を持つjava.sql.Dateに変換します.
java.util.Calendar 変換元の時刻値を持つjava.sql.Dateに変換します.
Number 変換元のlong値を時刻値として持つjava.sql.Dateに変換します.
String DATE_PATTERNフォーマットに従いjava.sql.Dateに変換します.
詳細は「日付・時刻のフォーマット」を参照してください.
java.sql.Time java.sql.Time -
java.util.Date 変換元の時刻値を持つjava.sql.Timeに変換します.
java.util.Calendar 変換元の時刻値を持つjava.sql.Timeに変換します.
Number 変換元のlong値を時刻値として持つjava.sql.Timeに変換します.
String TIME_PATTERNフォーマットに従いjava.sql.Timeに変換します.
詳細は「日付・時刻のフォーマット」を参照してください.
java.sql.Timestamp java.sql.Timestamp -
java.util.Date 変換元の時刻値を持つjava.sql.Timestampに変換します.
java.util.Calendar 変換元の時刻値を持つjava.sql.Timestampに変換します.
Number 変換元のlong値を時刻値として持つjava.sql.Timestampに変換します.
String TIMESTAMP_PATTERNフォーマットに従いjava.sql.Timestampに変換します.
詳細は「日付・時刻のフォーマット」を参照してください.
配列 配列 変換元と同じ大きさの配列に変換します.
変換元配列の要素は変換先配列の要素型に変換します.
java.util.Collection 変換元と同じ大きさの配列に変換します.変換元が順序のあるコレクション(java.util.Listjava.util.LinkedHashSetなど)の場合,順序は維持されます.
変換元の要素は変換先配列の要素型に変換します.
その他任意の型 変換元を変換先配列の要素型に変換したオブジェクトを唯一の要素とする配列に変換します.
java.util.List java.util.List -
配列 変換元と同じ大きさのリストに変換します.
変換元配列の要素はそのまま変換先リストの要素になります.
java.util.Collection 変換元と同じ大きさのリストに変換します.変換元が順序のあるコレクション(java.util.Listjava.util.LinkedHashSetなど)の場合,順序は維持されます.
変換元コレクションの要素はそのまま変換先リストの要素になります.
その他任意の型 変換元を変換先配列の要素型に変換したオブジェクトを唯一の要素とするリストに変換します.
java.util.Set java.util.Set -
配列 変換元と同じ大きさのセット(java.util.LinkedHashSet)に変換します.配列の順序は維持されます.
変換元配列の要素はそのまま変換先セットの要素になります.
java.util.Collection 変換元と同じ大きさのセット(java.util.LinkedHashSet)に変換します.変換元が順序のあるコレクション(java.util.Listjava.util.LinkedHashSetなど)の場合,順序は維持されます.
変換元コレクションの要素はそのまま変換先リストの要素になります.
その他任意の型 変換元を変換先配列の要素型に変換したオブジェクトを唯一の要素とするリストに変換します.
JavaBeans JavaBeans 変換元JavaBeansを変換先JavaBeansに変換します.
変換元JavaBeansのプロパティは変換先JavaBeansの対応するプロパティに変換されます.
Number Enumのサブクラス 変換元の数値を序数とする列挙定数に変換します (S2-Tigerが必要です).
変換はClass#getEnumConstants()が返す配列を使って行われます.
その他任意の型 変換元の文字列表現を名前とする列挙定数に変換します (S2-Tigerが必要です).
変換はEnum#valueOf(Class, String)で行われます.

ネストしたJavaBeansのプロパティ

変換先のJavaBeansに存在するプロパティと同名のプロパティが変換元のJavaBeansに存在しない場合,S2DxoはネストしたJavaBeansのプロパティを探します.ネストしたJavaBeansのプロパティとは,変換元JavaBeansが持つJavaBeansを型とするプロパティのプロパティです.この探索は一段階のみ行われます.

public class Employee {
    private Department department;
    ・・・
}

public class Department {
    private String dname;
    ・・・
}

public class EmployeePage {
    private String dname;
    ・・・
}

上の例で,EmployeeからEmployeePageへの変換を行うと,EmployeePagednameプロパティには,Employeedepartmentプロパティが参照する,Departmentdnameプロパティが設定されます.

より深くネストしたプロパティを扱うには,「変換元プロパティ名の指定」を参照してください.

変換のカスタマイズ

変換元プロパティ名の指定

変換元JavaBeansまたはマップのプロパティ名と変換先JavaBeansまたはマップのプロパティ名が異なる場合はアノテーションで変換ルールを指定することができます.アノテーションはDXOのメソッドに定数アノテーションまたはTigerアノテーションで指定します.

変換ルールは文字列で,その内容は

destPropertyName : sourcePropertyName

という組をカンマ区切りで並べたものです.変換先のプロパティ名が前になるので注意してください.

変換元のプロパティ名には,ネストしたJavaBeansのプロパティ名をピリオド区切りで指定することもできます.

ename : name, dname : department.name

という変換ルールを指定した場合,変換先JavaBeansのenameプロパティには変換元JavaBeansのnameプロパティの値が,変換先JavaBeansのdnameプロパティには変換元JavaBeansのdepartmentプロパティに設定されているJavaBeansのnameプロパティの値が設定されます.必要があれば「型変換」に従って変換が行われます.ただし,departmentnullだと変換先JavaBeansのdnameプロパティにはnullが設定されます.

JavaBeansからMapに変換する場合,変換ルールを指定すると指定したプロパティだけが変換先のマップに追加されることになるので注意してください.

変換元プロパティ名の代わりにnullを指定することもできます。

ename : name, dname : null

という変換ルールを指定した場合,変換先JavaBeansのdnameプロパティにはnullが設定されます.

定数アノテーション

定数アノテーションでは,DXOインタフェースの文字列定数として次のように指定します.定数名はメソッド名の後ろに"_CONVERSION_RULE"をつけたものになります.

public interface FooDxo {
    String convert_CONVERSION_RULE = "ename : name, dname : department.name";
    EmployeePage convert(Employee employee);
}

DXOインタフェースがメソッドをオーバーロードしている場合は,引数型の非修飾名をアンダースコア区切りで並べて指定することもできます.引数型が配列型の場合は配列要素の型名の後ろに'$'を付加します.'$'は配列の次元と同じ数だけ並べます.

public interface BarDxo {
    String convert_Employee_CONVERSION_RULE = "ename : name";
    EmployeePage convert(Employee employee);

    String convert_Employee$_CONVERSION_RULE = "ename : name, dname : department.name";
    EmployeePage[] convert(Employee[] employees);

    DepartmentPage convert(Department department);
}

この場合,最初の変換ルールはconvert(Employee)メソッドにのみ適用され,2番目の変換ルールはconvert(Employee[])メソッドにのみ適用されます.convert(Department)メソッドにはどちらの変換ルールも適用されません.

引数型の指定されない変換ルールは,同じ名前のメソッド全てに適用されます.引数型を伴う変換ルールと引数型のない変換ルールが混在する場合は,引数型の一致する変換ルールのあるメソッドはその変換ルールが,引数型の一致する変換ルールがないメソッドは引数型を伴わない変換ルールが適用されます.

Tigerアノテーション

Tigerアノテーションでは,DXOメソッドのアノテーションとして次のように指定します.

import org.seasar.extension.dxo.annotation.ConversionRule;
pubic interface BazDxo {
    @ConversionRule("ename : name, dname : department.name")
    EmployeePage convert(Employee employee);
}

OGNL式の利用

前述の変換ルールでは,OGNL式の評価結果を変換先JavaBeansのプロパティに設定することもできます.

この場合の変換ルールも文字列で,その内容は

'destPropertyName' : expression

という組をカンマ区切りで並べたものです.変換先のプロパティ名はシングルクオートで囲んでください.

OGNL式を利用する場合の変換ルールと「変換元プロパティ名の指定」のルールを混在することはできません.OGNL式を使用する場合は,ルール全体で変換先のプロパティ名をシングルクオートで囲む必要があります.

'name' : ename + '-' + dname

という変換ルールを指定した場合,変換先JavaBeansのnameプロパティには変換元JavaBeansのenameプロパティとdnameプロパティをハイフン区切りで連結した値が設定されます.OGNLE式の評価結果が変換先JavaBeansのプロパティ型と異なる場合は「型変換」に従って変換が行われます.

また,「変換元プロパティ名の指定」の次の例

dname : department.name

この例で変換元のdepartmentnullだった場合は変換時にNullPointerExceptionが発生してしまいます.departmentnullの場合は変換先のdnamenullを設定するには、以下のようにOGNL式の3項演算子を使用してください.

'dname' : department != null ? department.name : null

OGNL式の詳細については「OGNLガイド」を参照してください.

定数アノテーション

変換元プロパティ名の指定」と同様に指定します.

public interface FooDxo {
    String convert_CONVERSION_RULE = "'name' : ename + '-' + dname";
    EmployeePage convert(Employee employee);
}

Tigerアノテーション

変換元プロパティ名の指定」と同様に指定します.

import org.seasar.extension.dxo.annotation.ConversionRule;
public interface FooDxo {
    @ConversionRule("'name' : ename + '-' + dname")
    EmployeePage convert(Employee employee);
}

nullのプロパティを変換から除外する

変換元JavaBeansのプロパティまたはMapのマッピングの値がnullの場合は,変換先のJavaBeansまたはMapに値を設定しないことをアノテーションで指定することができます.アノテーションはDXOのメソッドに定数アノテーションまたはTigerアノテーションで指定します.

定数アノテーション

定数アノテーションでは,DXOインタフェースの文字列定数として次のように指定します.定数名はメソッド名の後ろに"_EXCLUDE_NULL"をつけたものになります.

public interface FooDxo {
    String convert_EXCLUDE_NULL = null;
    EmployeePage convert(Employee employee);
}

DXOインタフェースがメソッドをオーバーロードしている場合は,「変換元プロパティ名の指定」と同様に,引数型の非修飾名をアンダースコア区切りで並べて指定することもできます.

Tigerアノテーション

Tigerアノテーションでは,DXOメソッドのアノテーションとして次のように指定します.

import org.seasar.extension.dxo.annotation.ExcludeNull;
pubic interface BazDxo {
    @ExcludeNull
    EmployeePage convert(Employee employee);
}

空白文字列のプロパティを変換から除外する

変換元JavaBeansのプロパティまたはMapのマッピングの値が空白(スペース,タブ,復帰,改行)のみの文字列の場合は,変換先のJavaBeansまたはMapに値を設定しないことをアノテーションで指定することができます.アノテーションはDXOのメソッドに定数アノテーションまたはTigerアノテーションで指定します.

定数アノテーション

定数アノテーションでは,DXOインタフェースの文字列定数として次のように指定します.定数名はメソッド名の後ろに"_EXCLUDE_WHITESPACE"をつけたものになります.

public interface FooDxo {
    String convert_EXCLUDE_WHITESPACE = null;
    EmployeePage convert(Employee employee);
}

DXOインタフェースがメソッドをオーバーロードしている場合は,「変換元プロパティ名の指定」と同様に,引数型の非修飾名をアンダースコア区切りで並べて指定することもできます.

Tigerアノテーション

Tigerアノテーションでは,DXOメソッドのアノテーションとして次のように指定します.

import org.seasar.extension.dxo.annotation.ExcludeWhitespace;
pubic interface BazDxo {
    @ExcludeSpace
    EmployeePage convert(Employee employee);
}

プレフィックス付きのプロパティ

変換元のJavaBeansまたはMapからプレフィックスの付いたプロパティまたはキーのみを変換変換対象としたり,変換元のプロパティ名にプレフィックスを付けて変換対象としたりすることができます.プレフィックスはDXOのメソッドに定数アノテーションまたはTigerアノテーションで指定します.

ソースプレフィックス

ソースプレフィックスが指定されると,変換元JavaBeansのプロパティまたはMapのキーの中で,ソースプレフィックスで始まる物だけが変換の対象となります.変換先のJavaBeansまたはMapへはソースプレフィックスを取り除いたプロパティまたはキーで設定されます.

変換元として次のようなクラスがあるとします.

public class SearchPage {
    private String search_name_GE;
    private String search_name_LE;
    private String search_name_STARTS;
    private String search_name_ENDS;
    ...
}

変換先は次のようなクラスです.

public clsss EmpDto {
    private String name_GE;
    private String name_LE;
    private String name_STARTS;
    private String name_ENDS;
    ...
}

このような場合に,'search_'で始まるプロパティだけを対象として,変換先の'search_'を取り除いた名前のプロパティに設定することができます.

定数アノテーション

定数アノテーションでは,DXOインタフェースの文字列定数として次のように指定します.定数名はメソッド名の後ろに"_SOURCE_PREFIX"をつけたものになります.

public interface FooDxo {
    String convert_SOURCE_PREFIX = "search_";
    EmpDto convert(SearchPage page);
}

DXOインタフェースがメソッドをオーバーロードしている場合は,「変換元プロパティ名の指定」と同様に,引数型の非修飾名をアンダースコア区切りで並べて指定することもできます.引数型が配列型の場合は配列要素の型名の後ろに'$'を付加します.'$'は配列の次元と同じ数だけ並べます.

Tigerアノテーション

Tigerアノテーションでは,DXOメソッドのアノテーションとして次のように指定します.

import org.seasar.extension.dxo.annotation.SourcePrefix;
public interface BarDxo {
    @SourcePrefix("search_")
    EmpDto convert(SearchPage page);
}

デスティネーションプレフィックス

デスティネーションプレフィックスが指定されると,変換元JavaBeansのプロパティまたはMapのキーに,デスティネーションプレフィックス付加した名前がで変換先のJavaBeansまたはMapに設定されます.

変換元として次のようなクラスがあるとします.

public clsss EmpDto {
    private String name_GE;
    private String name_LE;
    private String name_STARTS;
    private String name_ENDS;
    ...
}

変換先は次のようなクラスです.

public class SearchPage {
    private String search_name_GE;
    private String search_name_LE;
    private String search_name_STARTS;
    private String search_name_ENDS;
    ...
}

このような場合に,変換元のプロパティに'search_'を付加した名前の変換先プロパティに設定することができます.

定数アノテーション

定数アノテーションでは,DXOインタフェースの文字列定数として次のように指定します.定数名はメソッド名の後ろに"_DEST_PREFIX"をつけたものになります.

public interface FooDxo {
    String convert_DEST_PREFIX = "search_";
    void convert(EmpEto src, SearchPage dest);
}

DXOインタフェースがメソッドをオーバーロードしている場合は,「変換元プロパティ名の指定」と同様に,引数型の非修飾名をアンダースコア区切りで並べて指定することもできます.引数型が配列型の場合は配列要素の型名の後ろに'$'を付加します.'$'は配列の次元と同じ数だけ並べます.

Tigerアノテーション

Tigerアノテーションでは,DXOメソッドのアノテーションとして次のように指定します.

import org.seasar.extension.dxo.annotation.SourcePrefix;
public interface BarDxo {
    @SourcePrefix("search_")
    void convert(EmpDto src, SearchPage dest);
}

日付・時刻のフォーマット

java.util.Datejava.util.CalendarなどとStringとの相互変換を行う場合には,フォーマットパターンを指定することができます.フォーマットパターンはjava.text.SimpleDateFormatが解釈することができるパターン文字列で,DXOインタフェースまたはそのメソッドにアノテーションで指定することができます.アノテーションが指定されたメソッドで行われる変換の際に,JavaBeansのプロパティにjava.util.Datejava.util.CalendarなどとStringとの変換が必要になると,指定されたフォーマットパターンが使われます.フォーマットパターンが指定されなかった場合は,デフォルトロケールのデフォルトパターンが使われます.

定数アノテーション

定数アノテーションでは,DXOインタフェースの文字列定数としてフォーマットパターンを指定します.定数名は次のいずれかで,最初に見つかったものが適用されます.

  1. methodName[_parameterType ...]_patternName
    指定された名前と引数並びを持つメソッドに固有のフォーマットパターンを指定します.
  2. methodName_patternName
    指定された名前を持つメソッドに固有のフォーマットパターンを指定します.
  3. patternName
    DXOインタフェース内の全メソッドに適用されるフォーマットパターンを指定します.

patternNameは次のいずれかです.

パターン名 適用される型
DATE_PATTERN java.util.Calendar
java.util.Date
java.sql.Date
TIME_PATTERN java.sql.Time
TIMESTAMP_PATTERN java.sql.Timestamp

次のようになります.

public interface FooDxo {
    String DATE_PATTERN = "yyyy-MM-dd";
    String TIME_PATTERN = "HH:mm:ss.SSS";
    String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss";

    String convert_DATE_PATTERN = "yyyy/MM/dd";
    String convert_Foo_TIME_PATTERN = "HH:mm:ss";
    String convert2_TIMESTAMP_PATTERN = "yyyy/MM/dd HH:mm:ss.SSS";

    Bar convert(Foo foo);
    Foo convert(Bar bar);
    Bar convert2(Foo foo);
}

上の例では,次の表のように定数アノテーションが適用されます.

メソッド DATE_PATTERN TIME_PATTERN TIMESTAMP_PATTERN
convert(Foo) convert_DATE_PATTERN convert_Foo_TIME_PATTERN TIMESTAMP_PATTERN
convert(Bar) convert_DATE_PATTERN TIME_PATTERN TIMESTAMP_PATTERN
convert2(Foo) DATE_PATTERN TIME_PATTERN TIMESTAMP_PATTERN

Tigerアノテーション

Tigerアノテーションでは,DXOのメソッドまたはインタフェースのアノテーションとして指定します.

アノテーション 適用される型
org.seasar.extension.dxo.DatePattern java.util.Calendar
java.util.Date
java.sql.Date
org.seasar.extension.dxo.TimePattern java.sql.Time
org.seasar.extension.dxo.TimestampPattern java.sql.Timestamp

次のようになります.

@DatePattern("yyyy-MM-dd")
@TimePattern("HH:mm:ss.SSS")
@TimestampPattern("yyyy-MM-dd HH:mm:ss")
public interface FooDxo {
    @DatePattern("yyyy/MM/dd")
    @TimePattern("HH:mm:ss")
    Bar convert(Foo foo);

    @DatePattern("yyyy/MM/dd")
    Foo convert(Bar bar);

    @TimestampPattern("yyyy/MM/dd HH:mm:ss.SSS")
    Bar convert2(Foo foo);
}

上の例では,定数アノテーションの例と同じになるようにフォーマットパターンを指定しています.

独自のコンバータ

プロパティやマップの要素の型が異なる場合はコンバータによって型変換されます.独自のコンバータを作成することにより,「型変換」に記述されていない独自の型をサポートすることができます.

コンバータの作成

コンバータはorg.seasar.extension.dxo.converter.Converterインタフェースを実装したクラスで,次のメソッドを持ちます.

Class[] getSourceClasses()
このコンバータがサポートしている変換元のクラスを配列で返します.
Class getDestClass()
このコンバータがサポートしている変換先のクラスを返します
Object convert(Object source, Class destClass, ConversionContext context)
sourcedestClassのインスタンスに変換して返します.
void convert(Object source, Object dest, ConversionContext context)
sourcedistに変換します.不変なオブジェクトへの変換を行うコンバータはこのメソッドを実装できないため,このメソッドが呼び出されるとUnsupportedOperationExceptionをスローします.

コンバータのクラス名は"DxoConverter"で終了するようにします.

以下は文字列 (String) を正規表現 (java.util.regex.Pattern) に変換するコンバータの例です.

public class PatternDxoConverter implements Converter {

    public Class[] getSourceClasses() {
        return new Class[] { String.class };
    }

    public Class getDestClass() {
        return Pattern.class;
    }

    public Object convert(Object source, Class destClass,
            ConversionContext context) {
        return Pattern.compile((String) source);
    }

    public void convert(Object source, Object dest, ConversionContext context) {
        throw new UnsupportedOperationException();
    }

}

独自作成したコンバータはdiconファイルに定義するか,AutoRegisterによりS2コンテナに自動登録されるようにします.

diconファイルへの登録

独自作成したコンバータは,diconファイルに登録して使用します.コンバータのコンポーネント名は"DxoConverter"で終了するようにします.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
    "http://www.seasar.org/dtd/components24.dtd">
<components>
  <component name="patternDxoConverter" instance="prototype"
    class="org.seasar.extension.dxo.annotation.impl.ConstantAnnotationReaderTest$HogeDxoConverter"/>
</components>

AutoRegisterを使って自動登録することもできます.コンポーネントとして登録されたコンバータはS2Dxoに組み込まれ,「型変換」に記述されている標準のコンバータと同じように動作します.

型変換」に記述されている標準のコンバータは,dxo-builtin-converters.diconおよびdxo-tiger-converters.diconに定義されています.これらのdiconファイルは,それぞれs2-extension-2.4.x.jarおよびs2-tiger-2.4.x.jarの中にバンドルされています.標準のコンバータを独自作成したコンバータに変更する場合はこれらのdiconファイルを置き換えてください.

アノテーションでコンバータを明示する

変換先JavaBeansのプロパティに特定のコンバータを使用することを明示することもできます.

定数アノテーションでは,変換先クラスに文字列定数として次のように指定します.定数名は,コンバータを適用するプロパティ名の後ろに"_コンバータ名"をつけたものになります.コンバータ名は独自作成したコンバータのコンポーネント名で,最後は"DxoConverter"で終了しなければなりません.

public class Dest {
  // namePattern プロパティは PatternDxoConverter を使って変換
  public static final namePattern_patternDxoConverter = null;
  private Pattern namePattern;
  ...
  public void setNamePattern(Pattern namePattern) {
    this.namePattern = namePattern;
  }
}

独自作成したコンバータにプロパティがあれば設定することもできます.

  public static final name_prefixDxoConverter = "prefix='$$'";

Tigerアノテーションの場合は,独自コンバータのためのアノテーションを作成します.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@DxoConverter("patternDxoConverter")
public @interface PatternDxoConverter {
}

変換先クラスのsetterメソッドまたはpublicフィールドに作成したアノテーションを次のように指定します.

public class Dest {
  private Pattern namePattern;
  ...
  @PatternDxoConverter
  public void setNamePattern(Pattern namePattern) {
    this.namePattern = namePattern;
  }
}

独自作成したコンバータにプロパティがある場合は,アノテーションにプロパティのための要素を持たせます.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@DxoConverter("prefixDxoConverter")
public @interface PrefixDxoConverter {
    String prefix();
}

変換先クラスのsetterメソッドに作成したアノテーションを次のように指定します.

  @PrefixDxoConverter(prefix="$$")
  public void setName(String name) {
    this.name = name;
  }

シャローコピー

Seasar2.4.17以前は,変換元と変換先が同じクラスの場合は,変換元のインスタンスをそのまま変換先として使用していました (シャローコピー的) が, 2.4.18からは,新しいインスタンスを作成するようになりました (ディープコピー的).

2.4.17以前とと同様の振る舞い (シャローコピー的) が必要な場合は,dxo.dicon でインクルードする dxo-builtin-converters.dicon を,dxo-builtin-converters-shallow.dicon に差し替えて使用してください.

dicon の差し替えは,s2container.dicon の中で次のように指定します.

<component class="org.seasar.framework.container.factory.SimplePathResolver">
  <initMethod name="addRealPath">
    <arg>"dxo-builtin-converters.dicon"</arg>
    <arg>"dxo-builtin-converters-shallow.dicon"</arg>
  </initMethod>
</component>