Dozer.mapperの使い方

## Dozerとは
Javaのマッピングフレームワークの一つ。
Dozerは、あるオブジェクトから別のオブジェクトにデータをコピーするために再帰を使用するマッピングフレームワークです。フレームワークは、Bean間でプロパティをコピーできるだけでなく、異なるタイプ間で自動的に変換することもできます。

## Dozerのメリット
* クラス間のbeenの受け渡しを簡略化できる
* beenの受け渡し時に値、型の変換ができる

## 設定方法(環境)
### Gradleプロジェクト
「build.gradle」ファイルに下記を記述
 
記述場所：dependencies
記述コード：

```gradle
implementation 'net.sf.dozer:dozer:5.5.1'
```

### Mavenプロジェクト（今回はこちらを主体に紹介していきます）

pom.xmlに下記を追記

```java
		<dependency>
			<groupId>net.sf.dozer</groupId>
			<artifactId>dozer</artifactId>
			<version>5.5.1</version>
		</dependency>
```

application.contextに下記を追記することで書くマッピングxmlを読み込むことができる。

```xml
<bean class="org.dozer.spring.DozerBeanMapperFactoryBean">
    <property name="mappingFiles"
        value="classpath*:/META-INF/dozer/**/*-mapping.xml" /><!-- (1) -->
</bean>
```


## Dozerの使い方
まずDozer.mapperを利用した場合と利用していない場合のコードの記載を比べてみます。

```java:Duzer.mapperなし
		input.setId(context.getId());
		input.setName(context.getName());
		input.setTitle(context.getTitle());
		input.setSubtitlle(context.getSubTitle());
		input.setBusinessDate(context.getBusinessDate());
```

```java:Dozer.mapperあり
		Mapper mapper = new DozerBeanMapper();
		mapper.map(context, input);
```

一目で分かる通り、記述が大幅に省略されています。
今回はbeenの階層構造なしですが、それでも何度もgetter,setterを記載せずにすみ、値の写かえ漏れを防ぎます。

概要も見たところで詳細に説明していく。

今回利用するbeenこれをServiceの呼び出しに必要なbeenにマッピングさせていく。

```java
@Data
public class ControllerContext {
	private int id;
	private String name;
	private String title;
	private String subTitle;
	private Date businessDate;
}
```
```java
@Data
public class ServiceContext {
	private int id;
	private String name;
	private String title;
	private String subtitlle;
	private Date businessDate;
}
```
コントローラのbeenに以下のように値を設定する。
その後Dozer.mapperにてマッピングを行う。

```java
		ServiceContext input = new ServiceContext();
		context.setId(2);
		context.setName("いいい");
		context.setTitle("タイトル２");
		context.setSubTitle("サブタイトル２");
		context.setBusinessDate(DateUtils.addDays(new Date(), 1));
		Mapper mapper = new DozerBeanMapper();
		mapper.map(context, input);
		System.out.println(input);
```

``` :出力結果
ServiceContext(id=2, name=いいい, title=タイトル２, subtitlle=サブタイトル, businessDate=Mon Oct 19 00:17:38 JST 2020)
```

| メソッド | 説明 |
|:-----------|------------|
| mapper.map()       | 第一引数に変換元のbeen名、第二引数に変換後のbeen名を記述することで使用できる   |

正常にマッピングされていることがわかる。
このようにしてbeenマッピングの記述を省略し、簡潔にプログラムを書くことができる。

## フィールド名が異なる場合のマッピング
Dozerのデフォルト設定では、コピー元と先で名前が同じプロパティを自動的にコピーします。
名前が異なるプロパティをコピーするには、カスタムマッピングが必要になります。

Dozerの設定はXMLとJava Configのいずれかで定義することができます。
使いたいほうを使ってもらえればOKですが、今回はXMLの場合の記述について記載します。

初めのMavenプロジェクトの場合のセットアップを実施していれば、```resouce/META-INF/dozer```配下の```-mapping.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">
    <!-- omitted -->
    <mapping>
        <class-a>com.example.mavenDemo.controller.ControllerForm</class-a>
        <class-b>com.example.mavenDemo.service.ServiceContext</class-b>
        <field>
              <a>controllerTitle</a>
              <b>title</b><!-- (1) -->
        </field>
    </mapping>
    <!-- omitted -->
</mappings>
```

| タグ | 説明 |
|-----------|------------|
| class-a       | <class-a>タグ内にコピー元のBeanの、完全修飾クラス名を指定する。        |
| class-b     | <class-b>タグ内にコピー先のBeanの、完全修飾クラス名を指定する。      |
| a       | ```<field>```タグ内の```<a>```タグ内にコピー元のBeanの、マッピング用のフィールド名を指定する。|
| b       | ```<field>```タグ内の```<b>```タグ内にclass-aに対応するコピー先のBeanの、マッピング用のフィールド名を指定する。 |

上記のカスタムマッピングの設定により、フィールド名の違いを緩和してくれる。

## データ型が違う場合のマッピング
データ型が異なる場合も既存のパターンであれば自動的に保管が行われます。
変換のパターンについては以下を参照してください。
[Basic Property Mapping](https://dozermapper.github.io/gitbook/documentation/simpleproperty.html#_Data_type_conversion)

先ほど例としてあげたマッピングの```id```を```int```にしたマッピング例を挙げてみる 

```java
@Data
public class ControllerForm {
	private String id;
	private String name;
	private String title;
	private String subTitle;
	private Date businessDate;
}

```

```java
@Data
public class ServiceContext {
	private int id;
	private String name;
	private String title;
	private String subTitle;
	private Date businessDate;
}
```

```java
		Mapper mapper = new DozerBeanMapper();
		mapper.map(context, input);
```

```:実行結果
ServiceContext(id=2, name=いいい, title=タイトル２, subTitle=サブタイトル２, businessDate=Mon Oct 19 17:12:25 JST 2020)
```

データ型が違う場合も正常にマッピングが行えていることがわかる。

## 参考

本記事ではDozerマッピングの触りの部分しか紹介することができなかった。
詳細については参考記事を参照してください。

[Javaマッピングフレームワークのパフォーマンス - Codeflow](https://www.codeflow.site/ja/article/java-performance-mapping-frameworks)
[Spring Boot キャンプ : Spring Boot + Dozer編](https://qiita.com/yoshikawaa/items/a6a69f2779cccf305cf1)
[Dozerの使い方](https://learning-collection.com/dozer%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9/)