weblog

技術的なメモ置き場。

【MapStruct】参照型オブジェクトのマッピング

ネストされた参照型クラスのマッピングメソッドがある場合は、自動的にネストしているクラスのマッピングでそのマッピングが使用される。

環境

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

Mapper

StudentにネストされているPersonのマッピングも定義する。

@Mapper
public interface SampleMapper {
    SampleMapper MAPPER = Mappers.getMapper(SampleMapper.class);

    Student toStudent(StudentEntity entity);

    Person toPerson(PersonEntity entity);
}

生成されたコード。 StudentのマッピングでPersonのマッピングが実行されている。

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

    @Override
    public Student toStudent(StudentEntity entity) {
        if ( entity == null ) {
            return null;
        }

        Student student = new Student();

        student.setPerson( toPerson( entity.getPerson() ) );

        return student;
    }

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

        Person person = new Person();

        person.setName( entity.getName() );

        return person;
    }
}

テストコード

@Test
public void test() {
    PersonEntity personEntity = new PersonEntity("マイケル");
    StudentEntity entity = new StudentEntity(personEntity);

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

    assertThat(student.getPerson().getName()).isEqualTo("マイケル");
}

Spring Test DBUnitでJSONカラムにデータを投入する

Spring Test DBUnit@DatabaseSetup を使用してテストデータを投入する際に、対象のテーブルにJSONカラムが存在すると失敗するが、PostgresqlDataTypeFactoryを拡張することで投入できるようになる。

環境

  • Spring Boot 1.4.1
    • Spring Boot Test Starter
  • Spring Test DBUnit 1.3.0
  • PostgreSQL 9.5

PostgresqlDataTypeFactoryの拡張

DBUnitのFeature RequestsにJSONカラムに対応するための以下の2つのクラスが置いてある。※ステータスがpendingであることに注意
https://sourceforge.net/p/dbunit/feature-requests/188/

  • JsonType.java
  • PostgresqlDataTypeFactory.java

JSONBに対応していないので、PostgresqlDataTypeFactory.javaの一部を修正する。

public DataType createDataType(int sqlType, String sqlTypeName) throws DataTypeException {
    // 省略
    if ("json".equals(sqlTypeName) || 
        "jsonb".equals(sqlTypeName) /* ←を追加*/ ) {
            return new JsonType()
    }
    // 省略
}

Configurationクラスの修正

上の2つのクラスを適当なパッケージに配置後、Configurationクラスの修正を行う。

@Configuration
public class TestConfig {
    @Bean
    public DataSource dataSource() {
        // 省略
    }

    @Bean
    public DatabaseConfigBean dbUnitDatabaseConfig() {
        DatabaseConfigBean dbConfig = new DatabaseConfigBean();
        dbConfig.setDatatypeFactory(new PostgresqlDataTypeFactory());
        return dbConfig;
    }

    @Bean
    public DatabaseDataSourceConnectionFactoryBean dbUnitDatabaseConnection() {
        DatabaseDataSourceConnectionFactoryBean dbConnection = new DatabaseDataSourceConnectionFactoryBean(dataSource());
        dbConnection.setDatabaseConfig(dbUnitDatabaseConfig());
        return dbConnection;
    }
}

これでJSONデータの投入が可能となる。

【MapStruct】暗黙的な型変換

今回は暗黙的な型変換についてまとめる。 異なる型のフィールドにマッピングする際に暗黙的な型変換が行われる。

環境

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

使用するBean

public class Dto {
    private int intValue;
    private long longValue;
    private double doubleValue;
    private boolean boolValue;
    private LocalDate localDateValue;
    // constructor/setter/getter
}

public class ConvertDto {
    private String intValue;
    private String longValue;
    private String doubleValue;
    private String boolValue;
    private String localDateValue;
    // constructor/setter/getter
}

プリミティブ型 -> String型

プリミティブ型(ラッパーも含む)はString型に変換される。

@Mapper
public interface ImplicitTypeConvertMapper {
    ImplicitTypeConvertMapper MAPPER = Mappers.getMapper(ImplicitTypeConvertMapper.class);

    ConvertDto toConvertDto(Dto dto);
}

テストコード

@Test
public void test() {
    Dto dto = new Dto();
    dto.setIntValue(1);
    dto.setLongValue(100L);
    dto.setDoubleValue(30.5);
    dto.setBoolValue(true);

    ConvertDto convertDto = ImplicitTypeConvertMapper.MAPPER.toConvertDto(dto);

    Assertions.assertThat(convertDto.getIntValue()).isEqualTo("1");
    Assertions.assertThat(convertDto.getLongValue()).isEqualTo("100");
    Assertions.assertThat(convertDto.getDoubleValue()).isEqualTo("30.5");
    Assertions.assertThat(convertDto.getBoolValue()).isEqualTo("true");

}

数値, 日付のフォーマット

数値や日付はフォーマットしてマッピングすることができる。 numberFormat dateFormat にフォーマットを指定する。

@Mapper
public interface ImplicitTypeConvertMapper {

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

    @Mapping(target = "intValue", numberFormat = "000")
    @Mapping(target = "localDateValue", dateFormat = "yyyy/MM/dd")
    ConvertDto format(Dto dto);
}

テストコード

@Test
public void test() {
    Dto dto = new Dto();
    dto.setIntValue(1);
    dto.setLocalDateValue(LocalDate.of(2020, 1, 20));

    ConvertDto convertDto = ImplicitTypeConvertMapper.MAPPER.format(dto);

    assertThat(convertDto.getIntValue()).isEqualTo("001");
    assertThat(convertDto.getLocalDateValue()).isEqualTo("2020/01/20");
}

他にも型変換の組み合わせがあるので、公式ドキュメントを参考。 http://mapstruct.org/documentation/stable/reference/html/#implicit-type-conversions