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..
}