【Java】データオブジェクトの入れ替えはマッパーでやってしまおう～BeanMapper Orika～

# Orikaとは
POJOやBeanにマッピング処理できるライブラリ。
マッピングの指定や型変換が楽に行えて便利。

- [Orika (github)](https://github.com/orika-mapper/orika)
- [Orika ユーザガイド](http://orika-mapper.github.io/orika-docs/)

# オブジェクトのプロパティを自動Mappingする
### 使用環境
|ビルドツール |フレームワーク|
|:----------:|:-----------:|
|Gradle      |Spring Boot  |

フレームワークはOrikaのオブジェクトをDIするために使用してます。（後述）

## ①ライブラリの使用準備
build.gradleのdependenciesに以下の1行を追加。
versionについては適宜読み替えてください

```gradle:build.gradle
compile group: 'ma.glasnost.orika', name: 'orika-core', version: '1.5.4'

```
<font color="Red">
**別のビルドツールをお使いの方は[このリンク先](https://mvnrepository.com/artifact/ma.glasnost.orika/orika-core/1.5.4)で使用環境に応じた記述を参照してください。**
</font>

## ② MapperFactoryをインスタンス化する

使用するクラスでnewしてもいいが、公式にはシングルトンにした方が
パフォーマンスいいみたいなので今回はSpringのDI機能を使用してシングルトン化しました。
※シングルトンってなんぞやって人は[ここ](https://www.techscore.com/tech/DesignPattern/Singleton.html/)でも見てくだせぇ

```java:ConvertMapper.class

@Componet
public class ConvertMapper {

  @Bean
  public MapperFactory getMapperFactory(){
    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    return mapperFactory;
  }
}

```

```java:Convert処理を行うクラスのフィールド変数
@Autowired
private MapperFactory mapperFactory;
```


##③ Convertの元クラスと先クラスを設定する
Convert元クラス名に変換元となるオブジェクト（データが設定されている方）のクラス名を設定、
Convert先クラス名に変換先となるオブジェクト（これから設定したい方）のクラス名を設定します。


```java:Convert処理を行うクラスのメソッド
 BoundMapperFacade<Convert元クラス名, Convert先クラス名> boundMapperFacade =
        mapperFactory.getMapperFacade(Convert元クラス名.class, Convert先クラス名.class);

```

##④ Convertの実施

```java:Convert処理を行うクラスのメソッド
Convert先のオブジェクト = boundMapperFacade.map(Convert元のオブジェクト);
```
これにてConvert完了です。


# オブジェクトのプロパティを手動Mappingする方法

## 
以下のような構造のクラスがありAからBに変換したい場合では、
**フィールド変数名が異なるため自動マッピングでは変換できません。**

|型    |Convert元|Convert先|
|:----:|:-------:|:------:|
|      |ClassA   |ClassB  |
|int   |id       |id      |
|String|propatyA |propatyB|

そのため、オブジェクトの変換を行う前に明示的に対となるプロパティ名を指定してあげる必要があります。
**※[オブジェクトのプロパティを自動Mappingする](https://qiita.com/NekozeDaisensei/items/92e9d5d653119a90f991#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%82%92%E8%87%AA%E5%8B%95mapping%E3%81%99%E3%82%8B)の手順③までは同じ**

```java:Convert処理を行うクラスのメソッド
mapperFactory.classMap(InputEstimateData.class, EstimateDataValue.class)
        .field("propatyA","propatyB") // マッピング対象となるプロパティ名を指定する。
        .byDefault()                  // 残りは自動Mappingする
        .register();

// Convertの実行も同じ
Convert先のオブジェクト = boundMapperFacade.map(Convert元のオブジェクト);
```

# Convertで型やデータの変換を行う
Convertにて特定の型を変換したい場合には以下のことを行う。

##①型変換用のクラスを作成する

変換条件に応じて下記のクラスを継承したクラスを作成する。

|単方向のみの変換                   |双方向の変換                                      |
|:--------------------------------|:-------------------------------------------------|
|ma.glasnost.orika.CustomConverter|ma.glasnost.orika.converter.BidirectionalConverter|

例は、java.sql.Timestamp型とjava.util.LocalDateTime型の双方向変換のクラス

```java:例：LocalDateTimeとTimestampの双方向変換
import java.sql.Timestamp;
import java.time.LocalDateTime;

public class DataConverter extends BidirectionalConverter<LocalDateTime, Timestamp> {
  @Override
  public Timestamp convertTo(LocalDateTime source, Type<Timestamp> destinationType, MappingContext mappingContext) {
    return Timestamp.valueOf(source);
  }

  @Override
  public LocalDateTime convertFrom(Timestamp source, Type<LocalDateTime> destinationType, MappingContext mappingContext) {
    return source.toLocalDateTime();
  }
}
```

##② ConverterFactoryに変換用のクラスを登録する
登録方法には2通りある。

方法1. すべての変換に対して適用する場合

```java:
ConverterFactory converterFactory = mapperFactory.getConverterFactory();
converterFactory.registerConverter(new DataConverter());
```

方法2. 変換時に対象のプロパティを指定する場合　

```java
ConverterFactory converterFactory = mapperFactory.getConverterFactory();
converterFactory.registerConverter("管理名",new DataConverter());
```

上記の内容をMapperFactoryのインスタンスを生成しているメソッドで設定する。

```java:ConvertMapper.class

@Componet
public class ConvertMapper {

  @Bean
  public MapperFactory getMapperFactory(){
    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    // ConverterFactoryの取得
    ConverterFactory converterFactory = mapperFactory.getConverterFactory();
        
    // 変換時に対象のプロパティを指定する場合
    converterFactory.registerConverter("管理名",new DataConverter());
    
    // すべての変換に対して適用する場合
    converterFactory.registerConverter(new DataConverter());
    
    return mapperFactory;
  }
}

```

##③ 変換の設定とConvertの実施

```java
BoundMapperFacade<Convert元クラス名, Convert先クラス名> boundMapperFacade =
        mapperFactory.getMapperFacade(Convert元クラス名.class, Convert先クラス名.class);

// 方法2の場合には変換条件を設定する（方法2の場合は不要）
// java.util.LocalDateTimeをjava.sql.Timestamp型に変換してマッピングする
mapperFactory.classMap(Convert元クラス名.class, Convert先クラス名.class)
  .fieldMap("Convert元クラスのマッピングさせたいプロパティ名","Convert先クラスのマッピングさせたいプロパティ名")
  .converter("ConverterFactoryに登録した時に指定した管理名")
  .add()
  .byDefault()
  .register();

// Convertの実行も同じ
Convert先のオブジェクト = boundMapperFacade.map(Convert元のオブジェクト);
```

#あとがき
わりかし便利だけどあまり表に出てこないBeanMapperのOrikaの使い方でした。
ここに書いてる内容でだいたいのことはできる感じです。

日の目を浴びないのはLombokですべてのプロパティに引数を設定するコンストラクタを作れって話ですが・・・
結局、それではGetter地獄からは抜け出せません。
Orikaはプロパティ名さえ同じであれば自動的にマッピングしてくれるのでこれはこれで便利ですね。