Skip to content

MapStruct

MapStruct 简介

MapStruct 是一个 Java 注释处理器,用于生成类型安全的 bean 映射类。

您所要做的就是定义一个映射器接口,该接口声明任何必需的映射方法。在编译期间,MapStruct 将生成该接口的实现。这个实现使用普通的 Java 方法调用在源和目标对象之间进行映射,即没有反射或类似。

MapStruct 依赖

MapStruct 包含以下依赖:

  • org.mapstruct:mapstruct - 包含必需的注解,如 @Mapping
  • org.mapstruct:mapstruct-processor - 包含生成 mapper 实现的注解处理器
xml
<properties>
    <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>${org.mapstruct.version}</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

基本映射

要创建一个映射器,只需用所需的映射方法定义一个 Java 接口,并用 org.mapstruct.Mapper 注解它。@Mapper 注解导致 MapStruct 代码生成器在构建期间创建一个 Mapper 接口的实现。

在生成的方法实现中,源类型的所有可读属性将被复制到目标类型的相应属性中:

  • 当一个属性与它的目标实体对应物具有相同的名称时,它将被隐式映射。
  • 当属性在目标实体中具有不同的名称时,可以通过 @Mapping 注释指定它的名称。

相同属性名

  • Source

    java
    package study.helloworld.mapstruct_samples.same_name;
    
    import java.time.LocalDate;
    
    public class Source {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
    
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.same_name;
    
    import java.time.LocalDate;
    
    public class Target {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
    
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.same_name;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface SourceTargetMapper {
        
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
    
        Target toTarget(Source source);
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.same_name;
    
    import java.time.LocalDate;
    
    public class SameNameDemo {
    
        public static void main(String[] args) {
            Source source = new Source();
            source.setName("小明");
            source.setAge(18);
            source.setBirthday(LocalDate.now());
            System.out.println(source);
            
            Target target = SourceTargetMapper.INSTANCE.toTarget(source);
            System.out.println(target);
        }
    }
    log
    Source [name=小明, age=18, birthday=2021-10-29]
    Target [name=小明, age=18, birthday=2021-10-29]

不同属性名

  • Source

    java
    package study.helloworld.mapstruct_samples.different_name;
    
    import java.time.LocalDate;
    
    public class Source {
    
        private String sourceName;
        
        private Integer sourceAge;
        
        private LocalDate sourceBirthday;
    
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.different_name;
    
    import java.time.LocalDate;
    
    public class Target {
    
        private String targetName;
    
        private Integer targetAge;
        
        private LocalDate targetBirthday;
    
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.different_name;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface SourceTargetMapper {
        
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
    
        @Mapping(source = "sourceName", target = "targetName")
        @Mapping(source = "sourceAge", target = "targetAge")
        @Mapping(source = "sourceBirthday", target = "targetBirthday")
        Target toTarget(Source source);
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.different_name;
    
    import java.time.LocalDate;
    
    public class DifferentNameDemo {
    
        public static void main(String[] args) {
            Source source = new Source();
            source.setSourceName("小明");
            source.setSourceAge(18);
            source.setSourceBirthday(LocalDate.now());
            System.out.println(source);
            
            Target target = SourceTargetMapper.INSTANCE.toTarget(source);
            System.out.println(target);
        }
    }
    log
    Source [sourceName=小明, sourceAge=18, sourceBirthday=2021-10-29]
    Target [targetName=小明, targetAge=18, targetBirthday=2021-10-29]

多个参数源的映射

  • Source

    java
    package study.helloworld.mapstruct_samples.several_source;
    
    import java.time.LocalDate;
    
    public class Source1 {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
    
        ...
    }
    
    package study.helloworld.mapstruct_samples.several_source;
    
    public class Source2 {
    
        private Double height;
        
        private Character gender;
    
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.several_source;
    
    import java.time.LocalDate;
    
    public class Target {
    
        private String targetName;
        
        private Integer age;
        
        private LocalDate birthday;
    
        private Double targetHeight;
        
        private Character gender;
    
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.several_source;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface SourceTargetMapper {
        
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
    
        @Mapping(source = "source1.name", target = "targetName")
        @Mapping(source = "source2.height", target = "targetHeight")
        Target toTarget(Source1 source1, Source2 source2);
        
        @Mapping(source = "name", target = "targetName")
        @Mapping(source = "height", target = "targetHeight")
        Target toTarget(Source1 source1, Source2 source2, String name, Double height);
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.several_source;
    
    import java.time.LocalDate;
    
    public class SeveralSourceDemo {
    
        public static void main(String[] args) {
            Source1 source1 = new Source1();
            source1.setName("小明");
            source1.setAge(18);
            source1.setBirthday(LocalDate.now());
            
            Source2 source2 = new Source2();
            source2.setHeight(1.75D);
            source2.setGender('男');
            System.out.println(source1.toString() + ", " + source2.toString());
            
            Target target = SourceTargetMapper.INSTANCE.toTarget(source1, source2);
            System.out.println(target);
            
            System.out.println("-------------------------------------");
            
            target = SourceTargetMapper.INSTANCE.toTarget(source1, source2, "小白", 1.80D);
            System.out.println(target);
        }
    }
    log
    Source [name=小明, age=18, birthday=2021-10-29], Source2 [height=1.75, gender=男]
    Target [targetName=小明, age=18, birthday=2021-10-29, targetHeight=1.75, gender=男]
    -------------------------------------
    Target [targetName=小白, age=18, birthday=2021-10-29, targetHeight=1.8, gender=男]

嵌套的 bean 映射

  • Source

    java
    package study.helloworld.mapstruct_samples.nested_bean;
    
    import java.time.LocalDate;
    
    public class Source {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
        
        private NestedSource nestedSource;
    
        ...
    }
    
    package study.helloworld.mapstruct_samples.nested_bean;
    
    public class NestedSource {
    
        private Double height;
        
        private Character gender;
    
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.nested_bean;
    
    import java.time.LocalDate;
    
    public class Target {
    
        private String targetName;
        
        private Integer age;
        
        private LocalDate birthday;
        
        private TargetSource targetSource;
    
        ...
    }
    
    package study.helloworld.mapstruct_samples.nested_bean;
    
    public class TargetSource {
    
        private Double targetHeight;
        
        private Character gender;
    
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.nested_bean;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface SourceTargetMapper {
        
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
    
        @Mapping(source = "source.name", target = "targetName")
        @Mapping(source = "source.nestedSource.height", target = "targetSource.targetHeight")
        Target toTarget(Source source);
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.nested_bean;
    
    import java.time.LocalDate;
    
    public class NestedDemo {
    
        public static void main(String[] args) {
            Source source = new Source();
            source.setName("小明");
            source.setAge(18);
            source.setBirthday(LocalDate.now());
            
            NestedSource nestedSource = new NestedSource();
            nestedSource.setHeight(1.75D);
            nestedSource.setGender('男');
            source.setNestedSource(nestedSource);
            System.out.println(source);
            
            Target target = SourceTargetMapper.INSTANCE.toTarget(source);
            System.out.println(target);
        }
    }
    log
    Source [name=小明, age=18, birthday=2021-10-29, nestedSource=NestedSource [height=1.75, gender=男]]
    Target [targetName=小明, age=18, birthday=2021-10-29, targetSource=TargetSource [targetHeight=1.75, gender=男]]

更新现有的 bean

  • Source

    java
    package study.helloworld.mapstruct_samples.existing_bean;
    
    import java.time.LocalDate;
    
    public class Source {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
    
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.existing_bean;
    
    import java.time.LocalDate;
    
    public class Target {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
    
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.existing_bean;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.MappingTarget;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface SourceTargetMapper {
        
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
    
        void updateTarget(Source source, @MappingTarget Target target);
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.existing_bean;
    
    import java.time.LocalDate;
    
    public class ExistingBeanDemo {
    
        public static void main(String[] args) {
            Source source = new Source();
            source.setName("小明");
            source.setAge(18);
            source.setBirthday(LocalDate.now());
            System.out.println(source);
            
            Target target = new Target();
            target.setName("小花");
            target.setAge(28);
            target.setBirthday(LocalDate.now());
            
            SourceTargetMapper.INSTANCE.updateTarget(source, target);
            System.out.println(target);
        }
    }
    log
    Source [name=小明, age=18, birthday=2021-10-30]
    Target [name=小明, age=18, birthday=2021-10-30]

数据类型转换

  • Source

    java
    package study.helloworld.mapstruct_samples.data_type_conversions;
    
    import java.time.LocalDate;
    
    public class Source {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
        
        private Double height;
        
        private Character gender;
    
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.data_type_conversions;
    
    public class Target {
    
        private String name;
        
        private String age;
        
        private String birthday;
        
        private String height;
        
        private String gender;
    
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.data_type_conversions;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface SourceTargetMapper {
        
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
    
        @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy/MM/dd")
        @Mapping(source = "height", target = "height", numberFormat = ".000")
        Target toTarget(Source source);
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.data_type_conversions;
    
    import java.time.LocalDate;
    
    public class SameNameDemo {
    
        public static void main(String[] args) {
            Source source = new Source();
            source.setName("小明");
            source.setAge(18);
            source.setBirthday(LocalDate.now());
            source.setHeight(1.75D);
            source.setGender('男');
            System.out.println(source);
            
            Target target = SourceTargetMapper.INSTANCE.toTarget(source);
            System.out.println(target);
        }
    }
    log
    Source [name=小明, age=18, birthday=2021-10-30, height=1.75, gender=男]
    Target [name=小明, age=18, birthday=2021/10/30, height=1.750, gender=男]

映射集合容器

List 和 Set

  • Source

    java
    package study.helloworld.mapstruct_samples.collections;
    
    import java.time.LocalDate;
    
    public class Source {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
    
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.collections;
    
    public class Target {
    
        private String name;
        
        private String age;
        
        private String birthday;
    
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.collections;
    
    import java.util.List;
    import java.util.Set;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface SourceTargetMapper {
        
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
        
        Set<String> integerSetToStringSet(Set<Integer> integers);
    
        List<Target> toTargets(List<Source> sources);
        
        @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy/MM/dd")
        Target toTarget(Source source);
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.collections;
    
    import java.time.LocalDate;
    import java.util.List;
    import java.util.Set;
    
    public class CollectionsDemo {
    
        public static void main(String[] args) {
            
            Set<Integer> integers = Set.of(1, 2, 3, 4, 5);
            System.out.println(integers);
            
            Set<String> strings = SourceTargetMapper.INSTANCE.integerSetToStringSet(integers);
            System.out.println(strings);
            
            System.out.println("-------------------------------------");
            
            Source source = new Source();
            source.setName("小明");
            source.setAge(18);
            source.setBirthday(LocalDate.now());
            
            List<Source> sources = List.of(source);
            System.out.println(sources);
            
            List<Target> targets = SourceTargetMapper.INSTANCE.toTargets(sources);
            System.out.println(targets);
        }
    }
    log
    [1, 2, 3, 4, 5]
    [1, 2, 3, 4, 5]
    -------------------------------------
    [Source [name=小明, age=18, birthday=2021-10-30]]
    [Target [name=小明, age=18, birthday=2021/10/30]]

Map

  • Source

    java
    package study.helloworld.mapstruct_samples.maps;
    
    import java.time.LocalDate;
    
    public class Source {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
    
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.maps;
    
    public class Target {
    
        private String name;
        
        private String age;
        
        private String birthday;
    
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.maps;
    
    import java.util.Date;
    import java.util.Map;
    
    import org.mapstruct.MapMapping;
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface SourceTargetMapper {
        
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
        
        @MapMapping(valueDateFormat = "yyyy-MM-dd")
        Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
    
        Map<String, Target> toTargets(Map<String, Source> sources);
        
        @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy/MM/dd")
        Target toTarget(Source source);
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.maps;
    
    import java.time.LocalDate;
    import java.util.Date;
    import java.util.Map;
    
    public class MapsDemo {
    
        public static void main(String[] args) {
            
            Map<Long, Date> longDateMap = Map.of(1L, new Date());
            System.out.println(longDateMap);
            
            Map<String, String> stringStringMap = SourceTargetMapper.INSTANCE.longDateMapToStringStringMap(longDateMap);
            System.out.println(stringStringMap);
            
            System.out.println("-------------------------------------");
            
            Source source = new Source();
            source.setName("小明");
            source.setAge(18);
            source.setBirthday(LocalDate.now());
            
            Map<String, Source> sources = Map.of("小明", source);
            System.out.println(sources);
            
            Map<String, Target> targets = SourceTargetMapper.INSTANCE.toTargets(sources);
            System.out.println(targets);
        }
    }
    log
    {1=Sat Oct 30 17:20:53 CST 2021}
    {1=2021-10-30}
    -------------------------------------
    {小明=Source [name=小明, age=18, birthday=2021-10-30]}
    {小明=Target [name=小明, age=18, birthday=2021/10/30]}

映射枚举

  • Source

    java
    package study.helloworld.mapstruct_samples.enums;
    
    import java.time.LocalDate;
    
    public class Source {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
        
        private SourceEnum sourceEnum;
    
        ...
    }
    
    package study.helloworld.mapstruct_samples.enums;
    
    public enum SourceEnum {
    
        MALE('男'),
        
        FEMALE('女'),
        
        SECRECY('-');
        
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.enums;
    
    import java.time.LocalDate;
    
    public class Target {
    
        private String name;
        
        private Integer age;
        
        private LocalDate birthday;
        
        private TargetEnum targetEnum;
    
        ...
    }
    
    package study.helloworld.mapstruct_samples.enums;
    
    public enum TargetEnum {
    
        MAN('男'),
        
        WOMAN('女'),
        
        SECRECY('-');
        
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.enums;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.ValueMapping;
    import org.mapstruct.factory.Mappers;
    
    @Mapper
    public interface SourceTargetMapper {
        
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
        
        @ValueMapping(source = "MALE", target = "MAN")
        @ValueMapping(source = "FEMALE", target = "WOMAN")
        TargetEnum toTargetEnum(SourceEnum sourceEnum);
        
        @Mapping(source = "sourceEnum", target = "targetEnum")
        Target toTarget(Source source);
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.enums;
    
    import java.time.LocalDate;
    
    public class EnumsDemo {
    
        public static void main(String[] args) {
            
            Source source = new Source();
            source.setName("小明");
            source.setAge(18);
            source.setBirthday(LocalDate.now());
            
            SourceEnum sourceEnum = SourceEnum.MALE;
            source.setSourceEnum(sourceEnum);
            System.out.println(source);
            
            Target target = SourceTargetMapper.INSTANCE.toTarget(source);
            System.out.println(target);
        }
    }
    log
    Source [name=小明, age=18, birthday=2021-11-01, sourceEnum=MALE]
    Target [name=小明, age=18, birthday=2021-11-01, targetEnum=MAN]

使用装饰器自定义映射

  • Source

    java
    package study.helloworld.mapstruct_samples.decorators;
    
    import java.time.LocalDate;
    
    public class Source {
    
        private String name;
    
        private Integer age;
    
        private LocalDate birthday;
        
        ...
    }
  • Target

    java
    package study.helloworld.mapstruct_samples.decorators;
    
    import java.time.LocalDate;
    
    public class Target {
    
        private String name;
    
        private Integer age;
    
        private LocalDate birthday;
    
        private Boolean adult;
        
        ...
    }
  • Mapper

    java
    package study.helloworld.mapstruct_samples.decorators;
    
    import org.mapstruct.DecoratedWith;
    import org.mapstruct.Mapper;
    import org.mapstruct.factory.Mappers;
    
    @DecoratedWith(SourceTargetDecorator.class)
    @Mapper
    public interface SourceTargetMapper {
    
        SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
    
        Target toTarget(Source source);
    
    }
  • Decorator

    java
    package study.helloworld.mapstruct_samples.decorators;
    
    public abstract class SourceTargetDecorator implements SourceTargetMapper {
    
        private final SourceTargetMapper delegate;
    
        public SourceTargetDecorator(SourceTargetMapper delegate) {
            super();
            this.delegate = delegate;
        }
    
        @Override
        public Target toTarget(Source source) {
            Target target = delegate.toTarget(source);
            Boolean adult = target.getAge() != null
                    ? (target.getAge() >= 18 ? true : false)
                    : null;
            target.setAdult(adult);
            return target;
        }
    
    }
  • Demo

    java
    package study.helloworld.mapstruct_samples.decorators;
    
    import java.time.LocalDate;
    
    public class SameNameDemo {
    
        public static void main(String[] args) {
            Source source = new Source();
            source.setName("小明");
            source.setAge(18);
            source.setBirthday(LocalDate.now());
            System.out.println(source);
    
            Target target = SourceTargetMapper.INSTANCE.toTarget(source);
            System.out.println(target);
        }
    
    }
    log
    Source [name=小明, age=18, birthday=2021-11-02]
    Target [name=小明, age=18, birthday=2021-11-02, adult=true]

Released under the MIT License.