MapStruct Plus Documentation

repository·main·Indexed 19 days ago

https://github.com/linpeilie/mapstruct-plus

An enhancement tool for the MapStruct framework that simplifies Java Bean transformations by automating the generation of Mapper interfaces via annotations. It supports JDK 8-17 and Spring Boot 2-3, offering features such as automatic generation of mapping logic, Map-to-Object conversion, and compile-time efficiency using annotation processors. Key components include the @AutoMapper and @AutoMapping annotations and a Converter API for performing type conversions.

Tokens
30.8K
Snippets
68
Records
96
Agent score
61%

What's inside MapStruct Plus

  1. What is MapStruct Plus

    main

    MapStruct Plus is an enhancement tool for MapStruct designed to simplify Java Bean transformations. It builds upon MapStruct to automatically generate Mapper interfaces and provides additional features to make Java type conversion more convenient and elegant.

    Key Features:

    • Quick Setup: Requires only additional annotations for class conversion, reducing manual development.
    • Compile-time Efficiency: Uses annotation processors to perform all generation at compile time.
    • Property Conversion: Based on getter/setter methods.
    • Compatibility: Supports JDK 8~17 and Spring Boot 2~3.
    • Multi-class Conversion: Allows a single class to configure multiple type conversions.
    • Map-to-Object: Provides powerful functionality for converting between Maps and Objects.
  2. Key Features of MapStruct Plus

    main

    MapStruct Plus is an enhancement tool for MapStruct that automates the generation of Mapper interfaces and simplifies Java Bean conversion. Key capabilities include:

    • Automatic Generation: Reduces boilerplate by using annotations to handle conversions between Java classes.
    • Compile-time Efficiency: Uses annotation processors so all mapping logic is generated during the compilation phase.
    • Property Mapping: Performs conversions based on class getter/setter methods.
    • Compatibility: Supports JDK 8 through 17 and Spring Boot 2 through 3.
    • Multi-type Conversion: Allows a single class to be configured for multiple type conversions.
    • Map to Object: Provides enhanced functionality for converting Maps into Java objects.
  3. What is MapStruct

    main

    MapStruct is a Java annotation processor used to generate type-safe bean mapping implementations at compile time. Instead of writing manual, error-prone mapping code or using reflection-based dynamic mapping, you define a mapper interface with the required mapping methods, and MapStruct generates the implementation using pure Java methods.

    Key advantages include:

    • High Performance: Uses standard method calls instead of reflection, making it faster than dynamic mapping frameworks.
    • Compile-time Type Safety: Ensures only compatible objects and properties are mapped, preventing accidental mismatches (e.g., mapping an OrderEntity to a CustomerDTO).
    • Early Error Detection: Provides clear error reports during the build process if mappings are incomplete or if no suitable conversion method/type is found.
  4. What is MapStruct Plus

    main

    MapStruct Plus is an enhancement tool for MapStruct. It builds upon MapStruct to provide automatic generation of Mapper interfaces and strengthens specific functionalities to make Java type conversion more convenient and elegant.

    Key characteristics:

    • Compatibility: It embeds MapStruct and is fully compatible with it. If you are already using MapStruct, you can replace your existing dependencies with MapStruct Plus seamlessly.
    • Mechanism: Like MapStruct, it is a Java Annotation Processor based on JSR 269. It is triggered during the build process by tools such as Maven, Gradle, or Ant.
  5. Handle immutable types with @Immutable

    main

    For immutable types (classes where state cannot be changed after construction), the standard MapStruct pattern of T convert(S source, @MappingTarget T target) is ineffective because the target cannot be modified.

    Since version 1.3.2, you can use the @Immutable annotation (from any package) to mark a class as immutable. When a class is marked as @Immutable, the generated method signature for the target-based conversion will change to simply return the target without attempting to map properties into it:

    public T convert(S source, @MappingTarget T target) {
        return target;
    }
    @Immutable
    public class MyImmutableClass {
        // ...
    }
  6. Configure directory rules for generated classes

    main
    By default, generated classes are placed in the same package as the source class. However, if you are converting classes from external dependency packages, the generated classes will be placed in those external packages, which Spring might not scan. In such cases, you should use the Configuration guide to specify a specific directory for generated classes.
  7. Configure reverse property mapping with @ReverseAutoMapping

    main

    By default, adding @AutoMapper to a source class generates both source-to-target and target-to-source mappers. However, custom configurations (like @AutoMapping) applied to the source class are not automatically applied to the reverse (target-to-source) mapping.

    To define custom rules for the reverse mapping (Target $\rightarrow$ Source), use the @ReverseAutoMapping annotation on the source class.

    Key Details:

    • source and target parameters: In @ReverseAutoMapping, the source refers to the attribute in the target class, and target refers to the attribute in the source class.
    • Constraint: Once you use @ReverseAutoMapping on a class, you must not add any other custom conversion annotations (like @AutoMapping) to the target class to avoid configuration conflicts.
    • Alternative: The recommended way to handle custom reverse mapping is to add @AutoMapper directly to the target class as well. Use @ReverseAutoMapping only when the target class cannot access the source class or project standards prohibit adding annotations to the target class.
  8. Automatic conversion of custom nested objects

    main

    If a class contains a custom object type, MapStructPlus will automatically look for a corresponding conversion method for that type.

    For example, if Car has a SeatConfiguration field and CarDto has a SeatConfigurationDto field, MapStructPlus will first generate a mapper for SeatConfiguration $\rightarrow$ SeatConfigurationDto and then use it automatically during the Car $\rightarrow$ CarDto conversion process.

    @AutoMapper(target = CarDto.class)
    @Data
    public class Car {
        private SeatConfiguration seatConfiguration;
    }
    
    @Data
    public class CarDto {
        private SeatConfigurationDto seatConfiguration;
    }
    
    @Data
    @AutoMapper(target = SeatConfigurationDto.class)
    public class SeatConfiguration {
        // fields
    }
  9. Map using Builders (Immutables)

    main

    MapStruct supports mapping to immutable types using the BuilderProvider SPI. It looks for a pattern where a class has a static builder method that returns a builder instance, and the builder has a build() (or similar) method to create the final object.

    Supported Frameworks:

    • Lombok
    • AutoValue
    • Immutables
    • FreeBuilder
    • Custom hand-written builders (must follow the BuilderProvider rules)

    Configuration:

    • To disable builder detection and revert to standard getter/setter mapping, pass the compiler argument: -Amapstruct.disableBuilders=true.
    • You can specify a custom builder using the @Builder annotation in @BeanMapping, @Mapper, or @MapperConfig.
    // Example of a compatible Builder pattern
    public class Person {
        private final String name;
    
        protected Person(Person.Builder builder) {
            this.name = builder.name;
        }
    
        public static Person.Builder builder() {
            return new Person.Builder();
        }
    
        public static class Builder {
            private String name;
            public Builder name(String name) { 
                this.name = name; 
                return this; 
            }
            public Person create() { 
                return new Person(this); 
            }
        }
    }
  10. Automatic conversion of nested custom objects

    main

    If a property in the source class is a custom object, MapStructPlus will automatically look for and use an existing @AutoMapper configuration for that specific type to perform the nested conversion.

    For this to work, both the source nested type and the target nested type should have their own @AutoMapper annotations defining their relationship.

    @AutoMapper(target = CarDto.class)
    @Data
    public class Car {
        private SeatConfiguration seatConfiguration;
    }
    
    // Ensure SeatConfiguration also has its @AutoMapper defined
    @AutoMapper(target = SeatConfigurationDto.class)
    @Data
    public class SeatConfiguration {
        // fields
    }
  11. Use Mapping combinations (Experimental)

    main

    MapStruct allows using meta-annotations to reuse @Mapping configurations. You can define a custom annotation annotated with @Mapping and apply it to multiple mapper methods. This follows a "duck typing" approach: the target types must share the properties defined in the meta-annotation, and the source types must share the source properties.

    Warning: This is an experimental feature. Error messages may not clearly indicate which method is affected by a configuration error. For a safer alternative, use @InheritConfiguration with a common base class or interface.

    @Retention(RetentionPolicy.CLASS)
    @Mapping(target = "id", ignore = true)
    @Mapping(target = "creationDate", expression = "java(new java.util.Date())")
    @Mapping(target = "name", source = "groupName")
    public @interface ToEntity { }
    
    @Mapper
    public interface StorageMapper {
        StorageMapper INSTANCE = Mappers.getMapper( StorageMapper.class );
    
        @ToEntity
        @Mapping( target = "weightLimit", source = "maxWeight")
        ShelveEntity map(ShelveDto source);
    
        @ToEntity
        @Mapping( target = "label", source = "designation")
        BoxEntity map(BoxDto source);
    }
  12. Customize MapObjectConverter

    main

    Since version 1.5.2, you can fully customize the type conversion logic by implementing the MapObjectConverter interface. This allows you to remove the hutool-core dependency entirely. Custom converters must implement MapObjectConverter and provide a public no-argument constructor.

    Note for Spring Users: If you are using the Spring/Spring Boot component model (the default), your custom converter must be registered as a Spring Bean (e.g., using @Component).