2013-03-03 13:06:06 +01:00
2013-02-27 00:58:41 +01:00
2013-02-27 00:58:41 +01:00
2012-05-28 14:50:43 +02:00
2013-02-27 00:58:41 +01:00
2013-02-27 00:58:41 +01:00

What's MapStruct?

MapStruct is a Java annotation processor for the generation of type-safe bean mapping classes.

All you have to do is to define one more more mapper interfaces, annotate them with the @Mapper annotation and add the required mapping methods. During compilation, MapStruct will generate an implementation for each mapper interface, based on your preferred mapping framework (currently Dozer is supported, more to come).

The following shows an example. First, an object (e.g. a JPA entity) and an accompanying data transfer object (DTO):

public class Car {

	private String make;
	private int numberOfSeats;
	private int yearOfManufacture;
	
	//constructor, getters, setters etc.
}

public class CarDto {

	private String make;
	private int seatCount;
	private int yearOfManufacture;

	//constructor, getters, setters etc.
}

Both types are rather similar, only the seat count attributes have different names. The mapper interface thus looks like this:

@Mapper
public interface CarMapper {

	CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

	@Mapping(source = "numberOfSeats", target = "seatCount"),
	CarDto carToCarDto(Car car);

	Car carDtoToCar(CarDto carDto);
}

The interface is straight-forward:

  • Annotating it with @Mapper let's the MapStruct processor kick in during compilation
  • The INSTANCE member provides access to the mapper implementation for clients (a service loader based alternative coming soon)
  • For each mapping direction (entity to DTO and vice versa) there is a conversion method. For those attributes which have differing names and thus can't be mapped automatically, a mapping is configured using the @Mapping annotation on one of the methods.

Based on the mapper interface, clients can perform object mappings in a very easy and type-safe manner:

@Test
public void shouldMapCarToDto() {

	//given
	Car car = new Car( "Morris", 2, 1980 );

	//when
	CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );

	//then
	assertThat( carDto ).isNotNull();
	assertThat( carDto.getMake() ).isEqualTo( car.getMake() );
	assertThat( carDto.getSeatCount() ).isEqualTo( car.getNumberOfSeats() );
	assertThat( carDto.getyearOfManufacture() ).isEqualTo( car.getyearOfManufacture() );
}

Sometimes not only the names of two corresponding attributes differ, but also their types. This can be addressed by defining a custom type converter:

public class IntToStringConverter implements Converter<Integer, String> {

	@Override
	public String from(Integer source) {
		return source != null ? source.toString() : null;
	}

	@Override
	public Integer to(String target) {
		return target != null ? Integer.valueOf( target ) : null;
	}
}

To make use of a converter, specify its type within the @Mapping annotation:

@Mapper
public interface CarMapper {

	CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

	@Mappings({
		@Mapping(source = "numberOfSeats", target = "seatCount"),
		@Mapping(source = "yearOfManufacture", target = "manufacturingYear", converter = IntToStringConverter.class)
	})
	CarDto carToCarDto(Car car);

	Car carDtoToCar(CarDto carDto);
}

Using MapStruct

MapStruct is a Java annotation processor based on JSR 269 and as such can be used within command line builds (javac, Ant, Maven etc.) as well as from within your IDE.

Detailed instructions on the usage will be added soon, in between the set up of the Hibernate JPA meta model generator can be used as general guideline for setting up an annotation processor.

In order to use MapStruct, you have to check out its sources as it is currently not available in any public Maven repository.

What's next

MapStruct is just in its very beginnings. There are several ideas for further features, including but not limited to:

  • Allow to generate mappers for several existing mapping frameworks (currently only Dozer is supported).
  • Generate "native" mappers, that is without any reflection, but by direcly invoking getters and setters within the generated mapper. This should deliver very efficient mapper implementations
  • Provide a way to access the underlying mapper in order to make use of advanced features not provided by the MapStruct API (similar to the unwrap() method of JPA etc.)
  • Provide a way to add custom mapping code in a very simple way

Example:

@Mapper(extension=CarMapperCustomization.class)
public interface CarMapper {

	CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

	//automatically generated mapping methods
	CarDto carToCarDto(Car car);

	Car carDtoToCar(CarDto carDto);

	//very complex mapping which requires some hand-coding
	Vehicle carToVehicle(Car car);
}

public abstract class CarMapperCustomization implements CarMapper {

	@Override
	public Vehicle carToVehicle(Car car) {
		//implement custom mapping logic
	}
}
  • Remove runtime dependencies to MapStruct by using the JDK service loader to retrieve mapper implementations.
Description
An annotation processor for generating type-safe bean mappers
Readme 22 MiB
Languages
Java 98.5%
Fluent 0.8%
FreeMarker 0.7%