weblog

技術的なメモ置き場。

【MapStruct】 @Qualifierを使う

@Qualifier を使うことでアノテーションで独自の変換処理を追加することができる。

環境

  • MapStruct : 1.2.0.Final
  • Java : 9
  • JUnit : 4.12
  • AssertJ : 3.9.1

変換するクラスを用意する

public class Characters {

    public String upperCase(String string) {
        return (string == null) ? null : string.toUpperCase();
    }
}

アノテーションの作成

@Qualifier を合成したアノテーションを作成する。

@Qualifier
@Retention(CLASS)
@Target(TYPE)
public @interface CharacterConverter {
}
@Qualifier
@Retention(CLASS)
@Target(METHOD)
public @interface ToUpper {
}

アノテーションの付与

@CharacterConverter
public class Characters {

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

Mapperの作成

nameを大文字にする。

@Mapper(uses = Characters.class)
public interface QualifierMapper {
    QualifierMapper MAPPER = Mappers.getMapper(QualifierMapper.class);

    @Mapping(target = "name", qualifiedBy = { CharacterConverter.class, ToUpper.class })
    Student toStudent(StudentEntity entity);
}

テストコード

@Test
public void test() {
    StudentEntity entity = new StudentEntity("curry", 30, "gsw");

    Student student = QualifierMapper.MAPPER.toStudent(entity);

    assertThat(student.getName()).isEqualTo("CURRY");
    assertThat(student.getAddress()).isEqualTo("gsw");
    assertThat(student.getAge()).isEqualTo(30);
}

【MapStruct】@Context を使う

@Context パラメータを使用して、独自のカスタムメソッドを使用することができる。

環境

  • MapStruct : 1.2.0.Final
  • Java : 9
  • JUnit : 4.12
  • AssertJ : 3.9.1

Mapper

@Mapper
public interface ContextMapper {

    ContextMapper MAPPER = Mappers.getMapper(ContextMapper.class);

    PersonEntity toPersonEntity(Person person);

    PersonEntity toPersonEntity(Person person, @Context Locale locale);

    default String uppercase(String name, @Context Locale locale) {
        return name.toUpperCase(locale);
    }
}

生成されたMapper @Context パラメータがないメソッドにカスタムメソッドが適用されていないことがわかる。

/*
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2018-03-14T22:43:19+0900",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 9.0.1 (Oracle Corporation)"
)
*/
public class ContextMapperImpl implements ContextMapper {

    @Override
    public PersonEntity toPersonEntity(Person person) {
        if ( person == null ) {
            return null;
        }

        PersonEntity personEntity = new PersonEntity();

        personEntity.setName( person.getName() );
        personEntity.setAge( person.getAge() );

        return personEntity;
    }

    @Override
    public PersonEntity toPersonEntity(Person person, Locale locale) {
        if ( person == null ) {
            return null;
        }

        PersonEntity personEntity = new PersonEntity();

        personEntity.setName( uppercase( person.getName(), locale ) );
        personEntity.setAge( person.getAge() );

        return personEntity;
    }
}

テストコード

@Test
public void test1() {
    Person person = new Person("curry", 30);
    PersonEntity entity = ContextMapper.MAPPER.toPersonEntity(person, Locale.JAPAN);

    assertThat(entity.getName()).isEqualTo("CURRY");
    assertThat(entity.getAge()).isEqualTo(30);
}

@Test
public void test2() {
    Person person = new Person("curry", 30);
    PersonEntity entity = ContextMapper.MAPPER.toPersonEntity(person);

    assertThat(entity.getName()).isEqualTo("curry");
    assertThat(entity.getAge()).isEqualTo(30);
}

【MapStruct】他のMapperを使用する

独自にマッピングロジックを指定したいときに、独自のMapperを用意して使うことができる。

環境

  • MapStruct : 1.2.0.Final
  • Java : 9
  • JUnit : 4.12
  • AssertJ : 3.9.1

LocalDate <-> String 変換を行うMapper

public class DateMapper {

    public String asString(LocalDate date) {
        return date != null ? date.format(DateTimeFormatter.ISO_DATE) : null;
    }

    public LocalDate asDate(String date) throws ParseException {
        return date != null ? LocalDate.parse(date, DateTimeFormatter.ISO_DATE) : null;
    }
}

Mapper

@Mapper のuses属性に上記で作成したクラスを指定する。

@Mapper(uses = DateMapper.class)
public interface PersonMapper {
    PersonMapper MAPPER = Mappers.getMapper(PersonMapper.class);

    Person toPerson(PersonEntity entity);

    PersonEntity toPersonEnity(Person dto);
}

生成されたMapper

/*
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2018-03-10T23:10:12+0900",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 9.0.1 (Oracle Corporation)"
)
*/
public class PersonMapperImpl implements PersonMapper {

    private final DateMapper dateMapper = new DateMapper();

    @Override
    public Person toPerson(PersonEntity entity) {
        if ( entity == null ) {
            return null;
        }

        Person person = new Person();

        person.setName( entity.getName() );
        person.setBirthday( dateMapper.asString( entity.getBirthday() ) );

        return person;
    }

    @Override
    public PersonEntity toPersonEnity(Person dto) {
        if ( dto == null ) {
            return null;
        }

        PersonEntity personEntity = new PersonEntity();

        personEntity.setName( dto.getName() );
        try {
            personEntity.setBirthday( dateMapper.asDate( dto.getBirthday() ) );
        }
        catch ( ParseException e ) {
            throw new RuntimeException( e );
        }

        return personEntity;
    }
}

テストコード

@Test
public void test1() {
    PersonEntity entity = new PersonEntity("マイケル", LocalDate.of(1963, 2, 17));

    Person person = PersonMapper.MAPPER.toPerson(entity);

    assertThat(person.getName()).isEqualTo("マイケル");
    assertThat(person.getBirthday()).isEqualTo("1963-02-17");
}

@Test
public void test2() {
    Person person = new Person("マイケル", "1963-02-17");

    PersonEntity entity = PersonMapper.MAPPER.toPersonEnity(person);

    assertThat(entity.getName()).isEqualTo("マイケル");
    assertThat(entity.getBirthday()).isEqualTo(LocalDate.of(1963, 2, 17));
}