简介

MapStruct是一个代码生成器的工具类,简化了不同的Java Bean之间映射的处理,所以映射指的就是从一个实体变化成一个实体。在实际项目中,我们经常会将PO转DTO、DTO转PO等一些实体间的转换。在转换时大部分属性都是相同的,只有少部分的不同,这时我们可以通过mapStruct的一些注解来匹配不同属性,可以让不同实体之间的转换变的简单。
使用MapStruct在编译时会生成响应的转换实现类,所以没有性能损耗。
MapStruct官网地址: http://mapstruct.org/

搭建开发环境 –基于Maven

这里需要在配置文件pom.xml中进行如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
...
<properties>
    <!-- <org.mapstruct.version>1.1.0.Beta1</org.mapstruct.version>-->
      <org.mapstruct.version>1.1.0.Final</org.mapstruct.version>
</properties>
...
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-jdk8</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
     <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
   <!-- <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>-->
        </dependency>
 </dependencies>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
...

MapStruct实体间的转换

下面我们就来看看如何使用MapStruct实现实体之间的映射转换。下面两个类非常相似,有一个号码属性名不一样及在PeopleDTO中有个User对象,而在People中则是两个字符串属性。

PeopleEntity.java

1
2
3
4
5
6
7
8
9
10
public class PeopleEntity {
    private Integer age;
    private String name;
    private String callNumber;
    private String address;
    private String emile;

    //constructor, getters, setters etc.

}

PeopleDTO.java

1
2
3
4
5
6
7
8
public class PeopleDTO {
    private String phoneNumber;
    private String address;
    private String emile;
    private User  user;

    //constructor, getters, setters etc.
}

User.java

1
2
3
4
5
6
public class User {
    private Integer age;
    private String name;

    //constructor, getters, setters etc.
}

Mapper接口
要生成一个PeopleDTO与PeopleEntity对象相互转换的映射器,我们需要定义一个mapper接口。像这两个实体类有些属性不一样时,我们可以通过@Mapping注解来进行转换.

  • @Mapper注解标记这个接口作为一个映射接口,并且是编译时MapStruct处理器的入口。
    只有在接口加上这个注解, MapStruct 才会去实现该接口。
    @Mapper 里有个 componentModel 属性,主要是指定实现类的类型,一般用到两个 default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象
    spring:在接口的实现类上自动添加注解,@Component,可通过 @Autowired 方式注入。
  • @Mapping解决源对象和目标对象中,属性名字不同的情况。
  • Mappers.getMapper自动生成的接口的实现可以通过Mapper的class对象获取,从而让客户端可以访问Mapper接口的实现。
  • source:源属性
  • target:目标属性
  • dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为SimpleDateFormat的日期格式
  • ignore: 忽略这个字段
  • @Mappings:配置多个@Mapping
  • @MappingTarget 用于更新已有对象
  • @InheritConfiguration 用于继承配置
    这里只是列举了常用的字段和注解,详细的参考官方文档:http://mapstruct.org/documentation/stable/reference/html/

PeopleMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Mapper
public interface PeopleMapper {
    PeopleMapper INSTANCE = Mappers.getMapper(PeopleMapper.class);

    /**
     * PO转DTO
     *
     * @param entity PO
     * @return DTO
     */
    @Mapping(target = "phoneNumber", source = "callNumber")
    @Mapping(target = "user.name", source = "name")
    @Mapping(target = "user.age", source = "age")
    PeopleDTO entityToDTO(PeopleEntity entity);

    /**
     * DTO转PO
     *
     * @param peopleDTO DTO
     * @param entity    PO
     */
    @Mapping(target = "callNumber", source = "phoneNumber")
    @Mapping(target = "name", source = "user.name")
    @Mapping(target = "age", source = "user.age")
	// @MappingTarget告诉MapStruct在现有对象(PeopleEntity)上进行更新,而不是new一个新对象。
    void updateEntityFromDto(PeopleDTO peopleDTO, @MappingTarget PeopleEntity entity);

}

编译MapStruct

MapStruct是以Java编译器插件的形式来处理注解,生成mapper接口的实现。因此在使用之前我们要手工编译或启动程序时IDEA也会帮我们编译了,这里最好还是手动编译。

1
2
mvn compile
// 有的IDEA是  mvnw compile

编译完后的实现类可以到target目录看到PeopleMapperImpl.Java,如下图:

这时你也可以点进看里面代码的实现PeopleMapperImpl.Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class PeopleMapperImpl implements PeopleMapper {
    public PeopleMapperImpl() {
    }

    public PeopleDTO entityToDTO(PeopleEntity entity) {
        if(entity == null) {
            return null;
        } else {
            PeopleDTO peopleDTO = new PeopleDTO();
            User user = new User();
            peopleDTO.setUser(user);
            user.setAge(entity.getAge());
            user.setName(entity.getName());
            peopleDTO.setPhoneNumber(entity.getCallNumber());
            peopleDTO.setAddress(entity.getAddress());
            peopleDTO.setEmile(entity.getEmile());
            return peopleDTO;
        }
    }

    public void updateEntityFromDto(PeopleDTO peopleDTO, PeopleEntity entity) {
        if(peopleDTO != null) {
            entity.setName(this.peopleDTOUserName(peopleDTO));
            entity.setCallNumber(peopleDTO.getPhoneNumber());
            entity.setAge(this.peopleDTOUserAge(peopleDTO));
            entity.setAddress(peopleDTO.getAddress());
            entity.setEmile(peopleDTO.getEmile());
        }
    }

    private String peopleDTOUserName(PeopleDTO peopleDTO) {
        if(peopleDTO == null) {
            return null;
        } else {
            User user = peopleDTO.getUser();
            if(user == null) {
                return null;
            } else {
                String name = user.getName();
                return name == null?null:name;
            }
        }
    }

    private Integer peopleDTOUserAge(PeopleDTO peopleDTO) {
        if(peopleDTO == null) {
            return null;
        } else {
            User user = peopleDTO.getUser();
            if(user == null) {
                return null;
            } else {
                Integer age = user.getAge();
                return age == null?null:age;
            }
        }
    }
}

Mapper使用

最后我们就来看看Mapper的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@SpringBootApplication
public class MapperTestApplication {
    private static final Logger LOGGER = LoggerFactory.getLogger(MapperTestApplication.class);

    public static void main(String[] args) {

        //PO转DTO
        PeopleEntity peopleEntity = new PeopleEntity(18, "yoyo", "13215849",
                "shanghai ", "fdhf@163.com");
        PeopleDTO peopleDTO = PeopleMapper.INSTANCE.entityToDTO(peopleEntity);

        //DTO转PO
        User user = new User(21, "jack");
        PeopleDTO newP = new PeopleDTO("000000",
                "changsha ", "jack@163.com", user);
        PeopleEntity newEntity = new PeopleEntity();
        PeopleMapper.INSTANCE.updateEntityFromDto(newP, newEntity);


        LOGGER.info("PO转DTO peopleEntity==>" + peopleEntity.toString() + "\n peopleDTO==>" + peopleDTO.toString());
        LOGGER.info("DTO转PO PeopleDTO==>" + newP.toString() + "\n peopleDTO==>" + newEntity.toString());

        SpringApplication.run(MapperTestApplication.class, args);
    }
}

启动之后你可以在后台看到如下的输出

1
2
3
4
11:12:28.602 [main] INFO com.example.demo.MapperTestApplication - PO转DTO peopleEntity==>PeopleEntity{age=18, name='yoyo', callNumber='13215849', address='shanghai ', emile='fdhf@163.com'}
 peopleDTO==>PeopleDTO{phoneNumber='13215849', address='shanghai ', emile='fdhf@163.com', user=User{age=18, name='yoyo'}}
11:12:28.604 [main] INFO com.example.demo.MapperTestApplication - DTO转PO PeopleDTO==>PeopleDTO{phoneNumber='000000', address='changsha ', emile='jack@163.com', user=User{age=21, name='jack'}}
 peopleDTO==>PeopleEntity{age=21, name='jack', callNumber='000000', address='changsha ', emile='jack@163.com'}

通过从上面后台输出的数据我们可以看出,对于属性名称不同的情况、以及属性类型不同都自动帮助我们转换了,是不是感觉很神奇,是不是感觉很强大.再也不用自己写大量的代码进行实体间的转换了。

本文转载于:https://blog.csdn.net/lx_yoyo/article/details/75061614