MapStructで高速なオブジェクトマッピングをする

エキサイト株式会社メディア開発の佐々木です。

以前、下記の記事でJavaのオブジェクトマッピングツールでModelMapperを紹介しました。

excitech.hatenablog.com

ModelMapperは、とてもお手軽で大変便利なのですが、やや速度に問題があり大量のデータ処理等には不向きです。そこで MapStructを紹介します。

ライブラリ追加

build.gradleに下記を追加し、ライブラリを追加します。

dependencies {
  ...
        implementation 'org.mapstruct:mapstruct:1.4.2.Final'
        annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
        annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'   // lombokを併用する場合は必要
  ...
}

コード全体

コード全体は下記のようになります。

public class DemoMain {

    public static void main(String[] args) {
        InputModel inputModel = new InputModel();
        inputModel.setId(1);
        inputModel.setName("taro");

        RequestModel requestModel = RequestMapper.INSTANCE.toRequestModel(inputModel);
        System.out.println(requestModel);

        Request2Model request2Model = RequestMapper.INSTANCE.toRequest2Model(inputModel);
        System.out.println(request2Model);
    }

    @Data
    static class InputModel {
        private Integer id;
        private String name;
    }

    @Data
    static class RequestModel {
        private Integer id;
        private String name;
    }

    @Data
    static class Request2Model {
        private Integer no;
        private String title;
    }

    @Mapper
    interface RequestMapper {
        RequestMapper INSTANCE = Mappers.getMapper(RequestMapper.class);
        RequestModel toRequestModel(InputModel inputModel);

        @Mapping(source = "name", target = "title")
        @Mapping(source = "id", target = "no")
        Request2Model toRequest2Model(InputModel inputModel);
    }
}

出力結果

出力結果は下記になります。

DemoMain.RequestModel(id=1, name=taro)
DemoMain.Request2Model(no=1, title=taro)

ざっくり解説

データクラス定義

入力のクラスはこのように定義します。

    @Data
    static class InputModel {
        private Integer id;
        private String name;
    }

出力は、プロパティ名が同じものを用意します。異なったものでも可能なので、そのケースものも用意しておきます。

    // プロパティが同じクラス定義
    @Data
    static class RequestModel {
        private Integer id;
        private String name;
    }


    // プロパティが異なったクラス
    @Data
    static class Request2Model {
        private Integer no;
        private String title;
    }

マッピング処理の定義

マッピング処理はインターフェースに記述します。

    @Mapper
    interface RequestMapper {
        // Interfaceに定数を定義する
        RequestMapper INSTANCE = Mappers.getMapper(RequestMapper.class);

        // プロパティ名が同名の場合のメソッド定義
        RequestModel toRequestModel(InputModel inputModel);

        // プロパティ名が異なった場合のメソッド定義
        @Mapping(source = "name", target = "title")
        @Mapping(source = "id", target = "no")
        Request2Model toRequest2Model(InputModel inputModel);
    }

上記のようにインターフェースにアノテーションで定義を書いていきます。プロパティ名が同名である場合は、アノテーションは不要です。プロパティ名が異なった場合は、その数のアノテーション定義を書くことになります。

RequestMapperインタフェース内にオブジェクトの変換処理がまとめられるので、とても見通しがよくなります。また、ModelMapperよりはるかに高速です。(計測している記事) 毎回インターフェースを定義する必要があるので、ModelMapperよりは少々手間がかかりますが、大量データを処理する場合は、速度的に十分リターンがあると思いますので使ってみてください。

メディア開発では、中途採用をはじめ長期インターンの募集もしております。興味があればぜひお声がけください。

www.wantedly.com