2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

spring ModelMapperでのマッピングでnull の無視や厳密なマッピングの設定

Last updated at Posted at 2023-08-13

springでのアプリ開発で詰まったところをまとめていってます。
今回は、javaのorg.ModelMapperについてです。

1.この記事の内容

Bean同士のマッピングをしてくれるModelMapperで、
関係ないフィールドにもマッピングされたり、nullの値をマッピングさせない方法で少し躓いたので、まとめます。

2.そもそもModelMepperとは、、?

ModelMapperの役割的なことは、こちらの記事がわかりやすくまとめてくださっているので、読んでみてください。

簡単に要約すると、
違う型のBean(インスタンス)同士の値をコピーしてくれるものです。

3. 違うフィールド名にも反映されてしまう

そんなModelMapperのぶち当たった課題

課題点

まず一つ目は、謎に他のフィールドにもマッピングされる問題。

私は、下記二つが、フィールドでマッピングさせたかったBeanです。

BeanA.java
@Data
public class BeanA {
    private Integer hogehogeId;
    private Integer hogeId;
    private Integer fugaId;
    private String str;
}
BeanB.java
@Data
public class BeanB {
    //'hogehogeId'のBeanBバージョンがない
    //他は同じフィールド名
    private Integer hogeId;
    private Integer fugaId;
    private String str;
}

この二つのBeanの違いは、hogehogeIdがあるかないかだけです。

データの中身としてはこんな感じです。

BeanA
{
    "hogehoge_id": 111,
    "hoge_id": 222,
    "fuga_id": 333,
    "str": "text"
}
BeanB
{
    "hoge_id": 999,
    "uga_id": null,
    "str": null
}

で、下記コードでマッピング処理をしました。

MappinggService.java
@service
@RequiredArgsConstructor
public class MappingService {
    private final ModelMapper modelMapper;

public BeanA mapping() {
    //BeanA(destination)
    BeanA beanA = new BeanA();
    beanA.setHogehogeId(111);
    beanA.setHogeId(222);
    beanA.setFugaId(333);
    beanA.setStr("text");
    //BeanB(source)
    BeanB beanB = new BeanB();
    BeanB.setHogeId(999) //hogeIdだけセット
    //マッピング
    modelMapper.map(beanB, beanA);
    return beanA;
}

すると、結果がこうなりました。

マッピング結果
{
    "hogehoge_id": 999,
    "hoge_id": 999,
    "fuga_id": null,
    "str": null
}
マッピング理想
{
    "hogehoge_id": null,
    "hoge_id": 999,
    "fuga_id": null,
    "str": null
}

hogeIdだけが値がマッピングされて値が入ると思ったら、hogehogeIdにも同じ999がマッピングされていました。

解決方法

原因と考えられるのが、ModelMapperのデフォルトのConfigurationです。
config の中に、Matching strategy(マッチング戦略?)というものがあって、
デフォルト値はstandardで、結構曖昧なマッチング戦略になっています。

hogeIdhogehogeIdが似てるからマッピングされたのでしょうか。

具体的な解決策は、
ModelMapperの@Beanアノテーションでのコンポーネントをspringコンテナに登録する際に、
このマッチング戦略の設定をしました。

ModelMapperBean.java
public class ModelMapperBean{
    @Bean
    public ModelMapper modelMapper() {
		ModelMapper modelMapper = new ModelMapper();
        //マッチング戦略を厳しいものに設定
		modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        //型の完全マッチなど
		modelMapper.getConfiguration().setFullTypeMatchingRequired(true);
		return modelMapper;
	}
}

この設定にすることで、理想通りのマッピングになりました。

4.nullもマッピングされてしまう

続いて、二つ目の課題ですが、

課題点

先ほどの、マッピング前後をもう一度お見せします。

BeanA
{
    "hogehoge_id": 111,
    "hoge_id": 222,
    "fuga_id": 333,
    "str": "text"
}
BeanB
{
    "hoge_id": 999,
    "uga_id": null,
    "str": null
}

マッピング↓

マッピング結果
{
    "hogehoge_id": null,
    "hoge_id": 999,
    "fuga_id": null,
    "str": null
}

このnullです。
今回の要件的に、nullのものはマッピングを無視して、値がはいっているもののみをマッピングして、destenationに更新をかけるイメージでした。

しかし、当たり前ですが、sourceのnullをnullでdestinationにマッピングされてします。

いろいろ調べて、resource配下のapplication.yamlに

application.yaml
modelmapper:
    skip-null-enabled: true

にすると、nullをコピーしない設定にできるとあったのですが、
なぜかうまくいきませんでした。

バージョンの問題かなと思ったりして、
こちらを参考に、最新のバージョンに変えたりしたのですが、反映されず、、、
(原因わかっていません。思いつく方いらっしゃいましたら、ご教授ください。。)

解決策

これも、課題1の解決策と同様に、
ModelMapperのConfigurationに設定をすると解決しました。
あまり、 Beanの概念もわかっておらず、@Beanでのコンポーネント登録の際に、configもいじれることを知らなかったので、
課題1が解決した時に、もしやこの設定も!!?って感じに調べてみると、、
ありました!

ModelMapperBean.java
public class ModelMapperBean{
    @Bean
    public ModelMapper modelMapper() {
		ModelMapper modelMapper = new ModelMapper();
        //nullをスキップする設定
		modelMapper.getConfiguration().setSkipNullEnabled(true);
		return modelMapper;
	}
}

この設定で、理想通りのマッピングがされました。

マッピング結果
{
    "hogehoge_id": 111,
    "hoge_id": 999,
    "fuga_id": 333,
    "str": "text"
}

hoge_idだけがマッピングされました。

5.おわりに

他にも、いくつかconfig設定できるそうなので、このリファレンスもご参考ください。

どちらも、期待通りの挙動にできてよかったです。
初歩的な躓きですが、どなたかの参考に参考になれば幸いです。

誤っている箇所などあれば、なんなりとご指摘いただきたく。。
ご朗読、ありがとうございました。

2
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?