Java Bean マッパーライブラリ Dozer

Dozerとは

Dozer は Java Bean 間のマッパーライブラリです。あるオブジェクトから他のオブジェクトに、再帰的に値のコピーを行います。Dozer は、単純なプロパティマッピングから、複雑なタイプマッピング、単方向・双方向マッピング、暗黙的・明示的マッピングをサポートします。
2009年8月に5.1 がリリースされました。ライセンスはApache License Version 2.0となります。本家は以下となります。
http://dozer.sourceforge.net/

導入

以下のページよりDozerのアーカイブをダウンロードします。
http://sourceforge.net/projects/dozer/files/
ダウンロードしたファイルのJARファイル dist/dozer.jar をクラスパスに追加します。
その他に以下のcommons系のライブラリに依存しています。適宜ダウンロードしてクラスパスに追加します。

  • commons-lang(今回は2.4)
  • commons-logging(今回は1.1.1)
  • commons-collections(今回は3.2.1)
  • commons-beanutils(今回は1.8.0)

簡単なマッピング

最初の例では、ソースとなるデータオブジェクトの属性を、対象のオブジェクトの属性にコピーします。コピーする手順は以下のようにMapperのインスタンスのmapメソッドを呼び出すだけです。

    Mapper mapper = new DozerBeanMapper();
    DestinationObject destObject =  mapper.map(sourceObject, DestinationObject.class);

または、以下の様な使いかたもできます。

    Mapper mapper = new DozerBeanMapper();
    DestinationObject destObject = new DestinationObject();
    mapper.map(sourceObject, destObject);


実際のソースは以下のようになります。

package etc9;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;

public class DozerSample1 {

    public static void main(String[] args) {
        SourceObject sourceObject = new SourceObject();
        sourceObject.setName("hoge");
        sourceObject.setDate(new java.util.Date());

        Mapper mapper = new DozerBeanMapper();
        DestinationObject destObject =  mapper.map(sourceObject, DestinationObject.class);
        
        System.out.println(ToStringBuilder.reflectionToString(destObject));
    }
    
    public static class SourceObject {
        private String name;
        private java.util.Date date;
        
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public java.util.Date getDate() { return date; }
        public void setDate(java.util.Date date) { this.date = date; }
    }
    
    public static class DestinationObject {
        private String name;
        private String date;
        
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getDate() { return date; }
        public void setDate(String date) { this.date = date; }
    }
}

出力結果は以下のようになり、DestinationObjectに値がコピーされていることが分かります。

etc9.DozerSample1$DestinationObject@b988a6[name=hoge,date=Tue Sep 22 23:14:41 JST 2009]

Dozerマッピングを実行した後、destination オブジェクトの新しいインスタンスが作成され、source オブジェクトの同名フィールドの値がコピーされます。異なる型のフィールドが存在する場合、Dozerマッピングエンジンは自動的に型変換を行います。


現実的には、Mapper のインスタンスを毎回作成するのは望ましくありません。一般的には、1つのVMに対して、1つの Mapper インスタンスのみを持つべきです。SpringのようなIoCフレームワークを利用している場合には、singleton="true" と定義します。そうでない場合には、DozerBeanMapperSingletonWrappe クラスが提供されているので、これを使うことになるでしょう。

XMLによるカスタムマッピングの指定

異なる2つのオブジェクトのフィールド名や型が異なる場合、カスタムマッピングをXMLにて指定します。Dozerエンジンは、このカスタムマッピングXMLファイルを実行時に使用し、オブジェクトのマッピングを行います。マッピングを作成することで、Dozerエンジンは、定義したオブジェクト間のマッピングを双方向に解決します。(この時、フィールド名が同じものについては、マッピング定義を行う必要はありません)

マッピングの定義は以下のように、AとBのクラスを指定し、対応フィールドを列挙するだけです。

<mapping>
  <class-a>yourpackage.yourSourceClassName</class-a>
  <class-b>yourpackage.yourDestinationClassName</class-b>
    <field>
      <a>yourSourceFieldName</a>
      <b>yourDestinationFieldName</b>
    </field>
</mapping> 

では実際に試してみましょう。対象となるソースは以下です。ここでは、DozerBeanMapperSingletonWrapper を利用します。

package etc9;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.dozer.DozerBeanMapperSingletonWrapper;
import org.dozer.Mapper;

public class DozerSample1 {

    public static void main(String[] args) {
        SourceObject sourceObject = new SourceObject();
        sourceObject.setDate(new java.util.Date());

        Mapper mapper = DozerBeanMapperSingletonWrapper.getInstance();
        DestinationObject destObject =  mapper.map(sourceObject, DestinationObject.class);
        
        System.out.println(ToStringBuilder.reflectionToString(destObject));
    }
    
    public static class SourceObject {
        private java.util.Date date;
        public java.util.Date getDate() { return date; }
        public void setDate(java.util.Date date) { this.date = date; }
    }
    
    public static class DestinationObject {
        private String destDate;
        public String getDestDate() { return destDate; }
        public void setDestDate(String destDate) { this.destDate = destDate; }
    }
}

DozerBeanMapperSingletonWrapper を利用した場合、クラスパスのルートとなるディレクトリに(つまりEclipseで言うと、ソースフォルダの直下)に dozerBeanMapping.xml という名前でマッピングXMLを用意します。XMLファイルは以下のようになります。

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">

  <mapping>
    <class-a>etc9.DozerSample1$SourceObject</class-a>
    <class-b>etc9.DozerSample1$DestinationObject</class-b>
      <field>
        <a>date</a>
        <b>destDate</b>
      </field>
  </mapping> 
                   
</mappings>

ここではインナークラスを使用しているため、「etc9.DozerSample1$SourceObject」のような指定としていますが、通常は「.」でクラス名を指定することになるでしょう。


実行結果は以下のようになります。

etc9.DozerSample1$DestinationObject@10da5eb[destDate=Wed Sep 23 00:10:06 JST 2009]

String と Date のマッピング

StringとDateのマッピングには、Dateフォーマットの指定ができます。先の例で言うと、以下のように指定することができます。

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">

  <configuration>
    <stop-on-errors>true</stop-on-errors>
    <date-format>yyyy/MM/dd HH:mm</date-format>
    <wildcard>true</wildcard>
  </configuration>

  <mapping>
    <class-a>etc9.DozerSample1$SourceObject</class-a>
    <class-b>etc9.DozerSample1$DestinationObject</class-b>
      <field>
        <a>date</a>
        <b>destDate</b>
      </field>
  </mapping> 
                   
</mappings>

これにより、出力結果は以下の様になります。

etc9.DozerSample1$DestinationObject@1b3f829[destDate=2009/09/23 00:17]

マッピングファイルの指定

先の例では、DozerBeanMapperSingletonWrapper にてデフォルトのマッピングファイルを読み込んでいましたが、以下のようにマッピングファイルを指定することもできます。

    List<String> myMappingFiles = new ArrayList<String>();
    myMappingFiles.add("dozerBeanMapping.xml");
    DozerBeanMapper mapper = new DozerBeanMapper();
    mapper.setMappingFiles(myMappingFiles);
    DestinationObject destObject =  mapper.map(sourceObject, DestinationObject.class);

なお、クラスパス外のマッピングファイルを読み込むには「"file:c:\somedozermapping.xml" 」のように指定します。

Springとの統合

Spring設定ファイルに以下のようなDozerBeanMapperのbean定義を追加します。プロパティとして、mappingFilesを追加できます。

<bean id="mapper" class="org.dozer.DozerBeanMapper">
  <property name="mappingFiles">
    <list>
      <value>dozer-global-configuration.xml</value>			  
      <value>dozer-bean-mappings.xml</value>
    </list>
  </property>
</bean>

Dozer には Spring FactoryBean インターフェースを実装したDozerBeanMapperFactoryBeanが提供されており、以下の様な定義も可能。

<bean class="org.dozer.spring.DozerBeanMapperFactoryBean">
  <property name="mappingFiles">
    <list>
      <value>dozer-global-configuration.xml</value>
      <value>dozer-bean-mappings.xml</value>
    </list>
  </property>
</bean>