LoginSignup
16
13

More than 5 years have passed since last update.

MapStructの使い方

Posted at

MapStructの使い方についてまとめていく。
使用するバージョンは 1.2.0.Final

Mapperクラスの作成

Mapperクラスは、interfaceクラスまたはabstractクラスに @Mapper を付与して作成する。

@Mapper
public interface FooMapper {
    // omit..
}

@Mapper
public abstract FooMapper {
    // omit..
}

Mappingメソッドの定義

Mapperクラスを作成したらMappingメソッドを定義する。
@Mappingを使用し、target属性にマッピングされるフィールド名、source属性にマッピングするフィールド名を指定する。
フィールド名が同一なら省略できる。

@Mapper
public interface FooMapper {
    @Mapping(target="name", source="firstName")
    Bar fooToBar(Foo foo);
}

Mapperクラスのインスタンス

Mapperクラスのインスタンスは、 Mappers#getMapper で作成できる。

Mappers.getMapper(FooMapper.class);

コンポーネントモデルの指定

@MapperのcomponentModel属性を指定することでインスタンスの作成方法を変えることができる。
今回はSpring FrameworkでDIできるようにする。

// springを指定
@Mapper(componentModel = "spring")
public interface FooMapper {
    // omit..
}

自動生成されたMapperクラスを見てみると、@Componentが付与されていることがわかる。

import org.springframework.stereotype.Component;

@Component
public class FooMapperImpl implements FooMapper {
    // omit..
}

他の方法については公式ドキュメント参照
http://mapstruct.org/documentation/stable/reference/html/#retrieving-mapper

マッピング処理をカスタマイズする

MapStructには、マッピング処理をカスタマイズする方法がいくつか用意されている。

定数をマッピングする

@Mappingのconstant属性を使用する。

@Mapper
public interface FooMapper {
    @Mapping(target="name", constant = "ほげほげ")
    Bar fooToBar(Foo foo);
}

デフォルト値を設定する

@MappingのdefaultValue属性にデフォルト値を設定できる。
マッピングする値がnullの場合に適用される。

@Mapper
public interface FooMapper {
    @Mapping(target="name", defaultValue = "ほげほげ")
    Bar fooToBar(Foo foo);
}

任意のJavaコードを実行する

@MappingのExpression属性にマッピング処理に任意のJavaコードを指定することができる。
Javaコードはjava()で囲む。

@Mapper
public interface FooMapper {
    @Mapping(target="now", expression = "java(java.time.LocalDate.now())")
    Bar fooToBar(Foo foo);
}

@Mapperのimports属性を使用するとパッケージ名から記述しなくて良い。

@Mapper(imports = LocalDate.class)
public interface FooMapper {
    @Mapping(target="now", expression = "java(LocalDate.now())")
    Bar fooToBar(Foo foo);
}

数値をフォーマットする

@MappingのnumberFormat属性に数値のフォーマットを指定できる。

@Mapper
public interface FooMapper {
    @Mapping(target="num", numberFormat = "000") // ゼロ埋め
    Bar fooToBar(Foo foo);
}

日付をフォーマットする

@MappingのdateFormat属性に日付のフォーマットを指定できる。

@Mapper
public interface FooMapper {
    @Mapping(target="date", dateFormat = "yyyy/MM/dd")
    Bar fooToBar(Foo foo);
}

異なるEnum同士のマッピング

異なるEnumをマッピングすることができる。
Enumのマッピングには@ValueMappingを使用する。

@Mapper
public interface FooMapper {
    @ValueMapping(source = "SMALL", target = "SHORT")
        @ValueMapping(source = "MEDIUM", target = "TALL")
        @ValueMapping(source = "LARGE", target = "GRANDE")
    BarEnum fooToBar(FooEnum foo);
}

マッピングする対象がない場合に、taget属性にMappingConstants.NULLを指定することでnullを設定する。

@ValueMapping(source = "VENTI", target = MappingConstants.NULL)

デフォルト値を指定したい場合は、source属性にMappingConstants.ANY_REMAININGを指定する。

@ValueMapping(source = MappingConstants.ANY_REMAINING, target = "LARGE")

@Qualifierを使う

特別な振る舞いを追加したいときに使える。
例として大文字に変換する処理の追加を行う。

アノテーションの作成

@Qualifierを合成したアノテーションをクラスレベル用とメソッドレベル用の2つ作成する。

クラスレベル用
@Qualifier
@Retention(CLASS)
@Target(TYPE)
public @interface Converter {
}
メソッドレベル用
@Qualifier
@Retention(CLASS)
@Target(METHOD)
public @interface ToUpper {
}

振る舞いの定義

振る舞いを定義するクラスを作成する。
このとき上で作成したアノテーションを付与する。

@Converter
public class StringConverter {
    @ToUpper
    public String upperCase(String string) {
        return (string == null) ? null : string.toUpperCase();
    }
}

Mapperの作成

@Mapperのuses属性に振る舞いを定義したクラスを指定する。
@MappingのqualifiedBy属性に2つのアノテーションクラスを指定し、マッピングに振る舞いを追加する。

@Mapper(uses = StringConverter.class)
public interface FooMapper {
    @Mapping(target="name", qualifiedBy = { Converter.class, ToUpper.class })
    Bar fooToBar(Foo foo);
}

@Contextを使う

@Contextを使用することでマッピングの挙動を外部から変更することができる。

Mapperの作成

マッピングメソッドの引数に @Contextを付与した引数を追加する。

@Mapper
public interface FooMapper {
    Bar fooToBar(Foo foo, @Context Locale locale);
}

カスタムメソッドの追加

@Contextが付与された引数を持つカスタムメソッドを定義する。
以下の例では、LocalDate型のフィールドは指定されたLocalに合わせてフォーマットされ、それがマッピングされる。

@Mapper
public interface FooMapper {
    Bar fooToBar(Foo foo, @Context Locale locale);

    default String format(LocalDate date, @Context Locale locale) {
        // Localに合わせたフォーマットをするなど
    }
}

Decoratorを使う

Decoratorを使うことで、マッピング処理をオーバーライドして特別な処理を追加することができる。

まず、Mapperクラスを作成する。

@Mapper
public interface FooMapper {
    Bar fooToBar(Foo foo);
}

Decoratorクラスの作成

Decoratorクラスは抽象クラスで作成し、カスタマイズするMapperのサブタイプとする。

public abstract class FooMapperDecorator implements FooMapper {

    private final FooMapper delegate;

    public FooMapperDecorator(FooMapper delegate) {
        this.delegate = delegate;
    }

    // カスタマイズしたいメソッドをオーバーライド
    @Override
    public Bar fooToBar(Foo foo) {
        Bar bar = delegate.fooToBar(foo);
        // 特別な処理を追加する
        return bar;
    }
}

Decoratorの適用

MapperクラスにDecoratorクラスを適用する。
適用するには、@DecoratedWithを使用する。

@Mapper
@DecoratedWith(FooMapperDecorator.class)
public interface FooMapper {
    Bar fooToBar(Foo foo);
}

マッピング処理の前後に独自の処理を追加する

@BeforeMapping@AfterMappingを使うことで、マッピング処理の前後に独自の処理を実行させることができる。

@Mapper
public abstract class FooMapper {
    // マッピング前に実行したいメソッド
    @BeforeMapping
    protected void before(Foo foo) {
        // omit..
    }

    // マッピング後に実行したいメソッド
    @AfterMapping
    protected void after(@MappingTarget Bar bar) {
        // omit..
    }

    public abstract Bar fooToBar(Foo foo);
}

生成されたMapper

public class FooMapperImpl extends FooMapper {

    @Override
    public Bar fooToBar(Foo foo) {
        // マッピング前に実行
        before( foo );

        if ( foo == null ) {
            return null;
        }

        // マッピング処理

        // マッピング後に実行
        after( bar );

        return bar;
    }
}

マッピングの再利用

@InheritConfigurationを使用することで一度定義したマッピングを再利用することができる。

@Mapper(config = FooConfig.class)
public interface FooMapper {
    @Mapping(...)
    Bar fooToBar(Foo foo);

    @InheritConfiguration
    Bar fuga(Foo foo);
}

@InheritInverseConfigurationを使用すると逆向きのマッピングとして再利用できる。

@Mapper(config = FooConfig.class)
public interface FooMapper {
    @Mapping(...)
    Bar fooToBar(Foo foo);

    @InheritInverseConfiguration
    Foo barToFoo(Bar bar);
}

設定クラス

@MappingConfigで設定クラスを作成できる。
設定クラスではコンポーネントモデルの指定や、マッピングされていない項目があったときに警告やエラーにするのかなどの設定ができる。
詳細はJavadocを参照。
http://mapstruct.org/documentation/stable/api/org/mapstruct/MapperConfig.html

@MapperConfig(unmappedTargetPolicy = ReportingPolicy.IGNORE
     , mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_ALL_FROM_CONFIG)
public interface FooConfig {

}

作成したクラスをMapperクラスに設定する。
@Mapperのconfig属性に作成した設定クラスを指定する。

@Mapper(config = FooConfig.class)
public interface FooMapper {
    // omit..
}
16
13
0

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
16
13