mapstruct_mapstruct 转换 - CSDN
精华内容
参与话题
  • MapStruct实体间转换快速入门

    万次阅读 2017-07-13 14:55:23
    MapStruct是一个代码生成器的工具类,简化了不同的Java Bean之间映射的处理,所以映射指的就是从一个实体变化成一个实体。在实际项目中,我们经常会将PO转DTO、DTO转PO等一些实体间的转换。在转换时大部分属性都是...
    • 简介

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

    • 搭建开发环境 –基于Maven

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

    ...
    <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

    public class PeopleEntity {
        private Integer age;
        private String name;
        private String callNumber;
        private String address;
        private String emile;
    
        //constructor, getters, setters etc.
    
    }   

    PeopleDTO.java

    public class PeopleDTO {
        private String phoneNumber;
        private String address;
        private String emile;
        private User  user;
    
        //constructor, getters, setters etc.
    }

    User.java

    public class User {
        private Integer age;
        private String name;
    
        //constructor, getters, setters etc.
    }


    • Mapper接口

    要生成一个PeopleDTO与PeopleEntity对象相互转换的映射器,我们需要定义一个mapper接口。像这两个实体类有些属性不一样时,我们可以通过@Mapping注解来进行转换.
    1. @Mapper注解标记这个接口作为一个映射接口,并且是编译时MapStruct处理器的入口。
    2. @Mapping解决源对象和目标对象中,属性名字不同的情况。
    3. Mappers.getMapper自动生成的接口的实现可以通过Mapper的class对象获取,从而让客户端可以访问Mapper接口的实现。

    PeopleMapper.java

    @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")
        void updateEntityFromDto(PeopleDTO peopleDTO, @MappingTarget PeopleEntity entity);
    
    }


    • 编译MapStruct

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

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

    这里写图片描述

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

    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的使用
    @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);
        }
    }
    

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

    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'}
    

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

    展开全文
  • MapStruct超级简单的学习笔记

    万次阅读 热门讨论 2019-02-14 17:21:31
    MapStruct使用MapStruct1.MapStruct是用来做什么的?2.使用MapStruct解决上述问题3.添加默认方法4. 可以使用abstract class来代替接口5.可以使用多个参数5.直接使用参数作为属性值 使用MapStruct 首先来了解一下DTO...

    使用MapStruct

    首先来了解一下DTO,DTO简单的理解就是做数据传输对象的,类似于VO,但是VO用于传输到前端。(~~)

    1.MapStruct是用来做什么的?

    现在有这么个场景,从数据库查询出来了一个user对象(包含id,用户名,密码,手机号,邮箱,角色这些字段)和一个对应的角色对象role(包含id,角色名,角色描述这些字段),现在在controller需要用到user对象的id,用户名,和角色对象的角色名三个属性。一种方式是直接把两个对象传递到controller层,但是这样会多出很多没用的属性。更通用的方式是需要用到的属性封装成一个类(DTO),通过传输这个类的实例来完成数据传输。
    User.java

    @AllArgsConstructor
    @Data
    public class User {
        private Long id;
        private String username;
        private String password;
        private String phoneNum;
        private String email;
        private Role role;
    }
    

    Role.java

    @AllArgsConstructor
    @Data
    public class Role {
        private Long id;
        private String roleName;
        private String description;
    }
    
    

    UserRoleDto.java,这个类就是封装的类

    @Data
    public class UserRoleDto {
        /**
         * 用户id
         */
        private Long userId;
        /**
         * 用户名
         */
        private String name;
        /**
         * 角色名
         */
        private String roleName;
    }
    
    

    测试类,模拟将user对象转换成UserRoleDto对象

    public class MainTest {
        User user = null;
    
        /**
         * 模拟从数据库中查出user对象
         */
        @Before
        public void before() {
           Role role  = new Role(2L, "administrator", "超级管理员");
           user  = new User(1L, "zhangsan", "12345", "17677778888", "123@qq.com", role);
        }
    
        /**
         * 模拟把user对象转换成UserRoleDto对象
         */
        @Test
        public void test1() {
            UserRoleDto userRoleDto = new UserRoleDto();
            userRoleDto.setUserId(user.getId());
            userRoleDto.setName(user.getUsername());
            userRoleDto.setRoleName(user.getRole().getRoleName());
            System.out.println(userRoleDto);
        }
    }
    
    

    从上面代码可以看出,通过getter、setter的方式把一个对象属性值复制到另一个对象中去还是很麻烦的,尤其是当属性过多的时候。而MapStruct就是用于解决这种问题的。

    2.使用MapStruct解决上述问题

    这里我们沿用User.java、Role.java、UserRoleDto.java。
    新建一个UserRoleMapper.java,这个来用来定义User.java、Role.java和UserRoleDto.java之间属性对应规则:
    UserRoleMapper.java

    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.Mappings;
    import org.mapstruct.factory.Mappers;
    
    /**
     * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
     *          在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制
     */
    @Mapper
    public interface UserRoleMapper {
    
        /**
         * 获取该类自动生成的实现类的实例
         * 接口中的属性都是 public static final 的 方法都是public abstract的
         */
        UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);
    
        /**
         * 这个方法就是用于实现对象属性复制的方法
         *
         * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
         *
         * @param user 这个参数就是源对象,也就是需要被复制的对象
         * @return 返回的是目标对象,就是最终的结果对象
         */
        @Mappings({
                @Mapping(source = "id", target = "userId"),
                @Mapping(source = "username", target = "name"),
                @Mapping(source = "role.roleName", target = "roleName")
        })
        UserRoleDto toUserRoleDto(User user);
    
    }
    
    

    在测试类中测试:

    public class MainTest {
        User user = null;
    
        /**
         * 模拟从数据库中查出user对象
         */
        @Before
        public void before() {
           Role role  = new Role(2L, "administrator", "超级管理员");
           user  = new User(1L, "zhangsan", "12345", "17677778888", "123@qq.com", role);
        }
    
        /**
         * 模拟通过MapStruct把user对象转换成UserRoleDto对象
         */
        @Test
        public void test2() {
            UserRoleDto userRoleDto = UserRoleMapper.INSTANCES.toUserRoleDto(user);
            System.out.println(userRoleDto);
        }
    }
    

    通过上面的例子可以看出,使用MapStruct方便许多。

    3.添加默认方法

    添加默认方法是为了这个类(接口)不只是为了做数据转换用的,也可以做一些其他的事。

    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.Mappings;
    import org.mapstruct.factory.Mappers;
    
    /**
     * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
     *          在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制
     */
    @Mapper
    public interface UserRoleMapper {
    
        /**
         * 获取该类自动生成的实现类的实例
         * 接口中的属性都是 public static final 的 方法都是public abstract的
         */
        UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);
    
        /**
         * 这个方法就是用于实现对象属性复制的方法
         *
         * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
         *
         * @param user 这个参数就是源对象,也就是需要被复制的对象
         * @return 返回的是目标对象,就是最终的结果对象
         */
        @Mappings({
                @Mapping(source = "id", target = "userId"),
                @Mapping(source = "username", target = "name"),
                @Mapping(source = "role.roleName", target = "roleName")
        })
        UserRoleDto toUserRoleDto(User user);
    
        /**
         * 提供默认方法,方法自己定义,这个方法是我随便写的,不是要按照这个格式来的
         * @return
         */
        default UserRoleDto defaultConvert() {
            UserRoleDto userRoleDto = new UserRoleDto();
            userRoleDto.setUserId(0L);
            userRoleDto.setName("None");
            userRoleDto.setRoleName("None");
            return userRoleDto;
        }
    
    }
    
    

    测试代码:

    @Test
    public void test3() {
        UserRoleMapper userRoleMapperInstances = UserRoleMapper.INSTANCES;
        UserRoleDto userRoleDto = userRoleMapperInstances.defaultConvert();
        System.out.println(userRoleDto);
    }
    

    4. 可以使用abstract class来代替接口

    mapper可以用接口来实现,也可以完全由抽象来完全代替

    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.Mappings;
    import org.mapstruct.factory.Mappers;
    
    /**
     * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
     *          在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制
     */
    @Mapper
    public abstract class UserRoleMapper {
    
        /**
         * 获取该类自动生成的实现类的实例
         * 接口中的属性都是 public static final 的 方法都是public abstract的
         */
        public static final UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);
    
        /**
         * 这个方法就是用于实现对象属性复制的方法
         *
         * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
         *
         * @param user 这个参数就是源对象,也就是需要被复制的对象
         * @return 返回的是目标对象,就是最终的结果对象
         */
        @Mappings({
                @Mapping(source = "id", target = "userId"),
                @Mapping(source = "username", target = "name"),
                @Mapping(source = "role.roleName", target = "roleName")
        })
        public abstract UserRoleDto toUserRoleDto(User user);
    
        /**
         * 提供默认方法,方法自己定义,这个方法是我随便写的,不是要按照这个格式来的
         * @return
         */
        UserRoleDto defaultConvert() {
            UserRoleDto userRoleDto = new UserRoleDto();
            userRoleDto.setUserId(0L);
            userRoleDto.setName("None");
            userRoleDto.setRoleName("None");
            return userRoleDto;
        }
    
    }
    

    5.可以使用多个参数

    可以绑定多个对象的属性值到目标对象中:

    package com.mapstruct.demo;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.Mappings;
    import org.mapstruct.factory.Mappers;
    
    /**
     * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
     *          在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制
     */
    @Mapper
    public interface UserRoleMapper {
    
        /**
         * 获取该类自动生成的实现类的实例
         * 接口中的属性都是 public static final 的 方法都是public abstract的
         */
        UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);
    
        /**
         * 这个方法就是用于实现对象属性复制的方法
         *
         * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
         *
         * @param user 这个参数就是源对象,也就是需要被复制的对象
         * @return 返回的是目标对象,就是最终的结果对象
         */
        @Mappings({
                @Mapping(source = "id", target = "userId"),
                @Mapping(source = "username", target = "name"),
                @Mapping(source = "role.roleName", target = "roleName")
        })
        UserRoleDto toUserRoleDto(User user);
    
        /**
         * 多个参数中的值绑定 
         * @param user 源1
         * @param role 源2
         * @return 从源1、2中提取出的结果
         */
        @Mappings({
                @Mapping(source = "user.id", target = "userId"), // 把user中的id绑定到目标对象的userId属性中
                @Mapping(source = "user.username", target = "name"), // 把user中的username绑定到目标对象的name属性中
                @Mapping(source = "role.roleName", target = "roleName") // 把role对象的roleName属性值绑定到目标对象的roleName中
        })
        UserRoleDto toUserRoleDto(User user, Role role);
    

    对比两个方法~

    5.直接使用参数作为属性值

    package com.mapstruct.demo;
    
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.Mappings;
    import org.mapstruct.factory.Mappers;
    
    /**
     * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
     *          在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制
     */
    @Mapper
    public interface UserRoleMapper {
    
        /**
         * 获取该类自动生成的实现类的实例
         * 接口中的属性都是 public static final 的 方法都是public abstract的
         */
        UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);
    
        /**
         * 直接使用参数作为值
         * @param user
         * @param myRoleName
         * @return
         */
        @Mappings({
                @Mapping(source = "user.id", target = "userId"), // 把user中的id绑定到目标对象的userId属性中
                @Mapping(source = "user.username", target = "name"), // 把user中的username绑定到目标对象的name属性中
                @Mapping(source = "myRoleName", target = "roleName") // 把role对象的roleName属性值绑定到目标对象的roleName中
        })
        UserRoleDto useParameter(User user, String myRoleName);
    
    }
    
    

    测试类:

    public class Test1 {
        Role role = null;
        User user = null;
    
        @Before
        public void before() {
            role = new Role(2L, "administrator", "超级管理员");
            user = new User(1L, "zhangsan", "12345", "17677778888", "123@qq.com", role);
        }
        @Test
        public void test1() {
            UserRoleMapper instances = UserRoleMapper.INSTANCES;
            UserRoleDto userRoleDto = instances.useParameter(user, "myUserRole");
            System.out.println(userRoleDto);
        }
    }
    

    6.更新对象属性

    在之前的例子中UserRoleDto useParameter(User user, String myRoleName);都是通过类似上面的方法来生成一个对象。而MapStruct提供了另外一种方式来更新一个对象中的属性。@MappingTarget

    public interface UserRoleMapper1 {
    
        UserRoleMapper1 INSTANCES = Mappers.getMapper(UserRoleMapper1.class);
    
        @Mappings({
                @Mapping(source = "userId", target = "id"),
                @Mapping(source = "name", target = "username"),
                @Mapping(source = "roleName", target = "role.roleName")
        })
        void updateDto(UserRoleDto userRoleDto, @MappingTarget User user);
    
    
        @Mappings({
                @Mapping(source = "id", target = "userId"),
                @Mapping(source = "username", target = "name"),
                @Mapping(source = "role.roleName", target = "roleName")
        })
        void update(User user, @MappingTarget UserRoleDto userRoleDto);
    
    }
    

    通过@MappingTarget来指定目标类是谁(谁的属性需要被更新)。@Mapping还是用来定义属性对应规则。
    以此为例说明:

     @Mappings({
                @Mapping(source = "id", target = "userId"),
                @Mapping(source = "username", target = "name"),
                @Mapping(source = "role.roleName", target = "roleName")
        })
        void update(User user, @MappingTarget UserRoleDto userRoleDto);
    

    @MappingTarget标注的类UserRoleDto 为目标类,user类为源类,调用此方法,会把源类中的属性更新到目标类中。更新规则还是由@Mapping指定。

    7.没有getter/setter也能赋值

    对于没有getter/setter的属性也能实现赋值操作

    public class Customer {
    
        private Long id;
        private String name;
    
        //getters and setter omitted for brevity
    }
    
    public class CustomerDto {
    
        public Long id;
        public String customerName;
    }
    
    @Mapper
    public interface CustomerMapper {
    
        CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class );
    
        @Mapping(source = "customerName", target = "name")
        Customer toCustomer(CustomerDto customerDto);
    
        @InheritInverseConfiguration
        CustomerDto fromCustomer(Customer customer);
    }
    

    @Mapping(source = “customerName”, target = “name”)不是用来指定属性映射的,如果两个对象的属性名相同是可以省略@Mapping的
    MapStruct生成的实现类:

    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2019-02-14T15:41:21+0800",
        comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)"
    )
    public class CustomerMapperImpl implements CustomerMapper {
    
        @Override
        public Customer toCustomer(CustomerDto customerDto) {
            if ( customerDto == null ) {
                return null;
            }
    
            Customer customer = new Customer();
    
            customer.setName( customerDto.customerName );
            customer.setId( customerDto.id );
    
            return customer;
        }
    
        @Override
        public CustomerDto toCustomerDto(Customer customer) {
            if ( customer == null ) {
                return null;
            }
    
            CustomerDto customerDto = new CustomerDto();
    
            customerDto.customerName = customer.getName();
            customerDto.id = customer.getId();
    
            return customerDto;
        }
    }
    
    

    @InheritInverseConfiguration在这里的作用就是实现customerDto.customerName = customer.getName();功能的。如果没有这个注解,toCustomerDto这个方法则不会有customerName 和name两个属性的对应关系的。

    8.使用Spring依赖注入

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Customer {
        private Long id;
        private String name;
    }
    
    @Data
    public class CustomerDto {
        private Long id;
        private String customerName;
    }
    
    // 这里主要是这个componentModel 属性,它的值就是当前要使用的依赖注入的环境
    @Mapper(componentModel = "spring")
    public interface CustomerMapper {
    
        @Mapping(source = "name", target = "customerName")
        CustomerDto toCustomerDto(Customer customer);
    }
    
    

    @Mapper(componentModel = “spring”),表示把当前Mapper类纳入spring容器。可以在其它类中直接注入了:

    @SpringBootApplication
    @RestController
    public class DemoMapstructApplication {
    
    	// 注入Mapper
        @Autowired
        private CustomerMapper mapper;
    
        public static void main(String[] args) {
            SpringApplication.run(DemoMapstructApplication.class, args);
        }
    
        @GetMapping("/test")
        public String test() {
            Customer customer = new Customer(1L, "zhangsan");
            CustomerDto customerDto = mapper.toCustomerDto(customer);
            return customerDto.toString();
        }
    
    }
    

    看一下由mapstruct自动生成的类文件,会发现标记了@Component注解。

    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2019-02-14T15:54:17+0800",
        comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)"
    )
    @Component
    public class CustomerMapperImpl implements CustomerMapper {
    
        @Override
        public CustomerDto toCustomerDto(Customer customer) {
            if ( customer == null ) {
                return null;
            }
    
            CustomerDto customerDto = new CustomerDto();
    
            customerDto.setCustomerName( customer.getName() );
            customerDto.setId( customer.getId() );
    
            return customerDto;
        }
    }
    

    9.自定义类型转换

    有时候,在对象转换的时候可能会出现这样一个问题,就是源对象中的类型是Boolean类型,而目标对象类型是String类型,这种情况可以通过@Mapper的uses属性来实现:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Customer {
        private Long id;
        private String name;
        private Boolean isDisable;
    }
    
    @Data
    public class CustomerDto {
        private Long id;
        private String customerName;
        private String disable;
    }
    
    

    定义转换规则的类:

    public class BooleanStrFormat {
        public String toStr(Boolean isDisable) {
            if (isDisable) {
                return "Y";
            } else {
                return "N";
            }
        }
        public Boolean toBoolean(String str) {
            if (str.equals("Y")) {
                return true;
            } else {
                return false;
            }
        }
    }
    
    

    定义Mapper,@Mapper( uses = { BooleanStrFormat.class}),注意,这里的users属性用于引用之前定义的转换规则的类:

    @Mapper( uses = { BooleanStrFormat.class})
    public interface CustomerMapper {
    
        CustomerMapper INSTANCES = Mappers.getMapper(CustomerMapper.class);
    
        @Mappings({
                @Mapping(source = "name", target = "customerName"),
                @Mapping(source = "isDisable", target = "disable")
        })
        CustomerDto toCustomerDto(Customer customer);
    }
    

    这样子,Customer类中的isDisable属性的true就会转变成CustomerDto中的disable属性的yes。
    MapStruct自动生成的类中的代码:

    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2019-02-14T16:49:18+0800",
        comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)"
    )
    public class CustomerMapperImpl implements CustomerMapper {
    
    	// 引用 uses 中指定的类
        private final BooleanStrFormat booleanStrFormat = new BooleanStrFormat();
    
        @Override
        public CustomerDto toCustomerDto(Customer customer) {
            if ( customer == null ) {
                return null;
            }
    
            CustomerDto customerDto = new CustomerDto();
    		// 转换方式的使用
            customerDto.setDisable( booleanStrFormat.toStr( customer.getIsDisable() ) );
            customerDto.setCustomerName( customer.getName() );
            customerDto.setId( customer.getId() );
    
            return customerDto;
        }
    }
    
    

    要注意的是,如果使用了例如像spring这样的环境,Mapper引入uses类实例的方式将是自动注入,那么这个类也应该纳入Spring容器:
    CustomerMapper.java指定使用spring

    @Mapper(componentModel = "spring", uses = { BooleanStrFormat.class})
    public interface CustomerMapper {
    
        CustomerMapper INSTANCES = Mappers.getMapper(CustomerMapper.class);
    
        @Mappings({
                @Mapping(source = "name", target = "customerName"),
                @Mapping(source = "isDisable", target = "disable")
        })
        CustomerDto toCustomerDto(Customer customer);
    }
    
    

    转换类要加入Spring容器:

    @Component
    public class BooleanStrFormat {
        public String toStr(Boolean isDisable) {
            if (isDisable) {
                return "Y";
            } else {
                return "N";
            }
        }
        public Boolean toBoolean(String str) {
            if (str.equals("Y")) {
                return true;
            } else {
                return false;
            }
        }
    }
    

    MapStruct自动生成的类:

    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2019-02-14T16:55:35+0800",
        comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)"
    )
    @Component
    public class CustomerMapperImpl implements CustomerMapper {
    
    	// 使用自动注入的方式引入
        @Autowired
        private BooleanStrFormat booleanStrFormat;
    
        @Override
        public CustomerDto toCustomerDto(Customer customer) {
            if ( customer == null ) {
                return null;
            }
    
            CustomerDto customerDto = new CustomerDto();
    
            customerDto.setDisable( booleanStrFormat.toStr( customer.getIsDisable() ) );
            customerDto.setCustomerName( customer.getName() );
            customerDto.setId( customer.getId() );
    
            return customerDto;
        }
    }
    
    
    展开全文
  • 一文带你掌握Mapstruct用法

    千次阅读 2020-03-08 14:01:41
    MapStruct用途 在我们项目中,我们经常要处理将DTO转换成VO,DTO转成Entity等各类对象相互转换,如果我们采用BeanUtils工具类的copyProperty进行转换,很容易出现转换性能低,类型转换错误等问题。 与其他转换工具...

    MapStruct用途

    在我们项目中,我们经常要处理将DTO转换成VO,DTO转成Entity等各类对象相互转换,如果我们采用BeanUtils工具类的copyProperty进行转换,很容易出现转换性能低,类型转换错误等问题。
    与其他转换工具相对,MapStruct具有以下优点:
    通过使用普通方法调用而不是反射来快速执行
    编译时类型安全性:只能映射相互映射的对象和属性,不能将订单实体意外映射到客户DTO等。

    Maven配置

    ...
    <properties>
        <org.mapstruct.version>1.3.1.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</artifactId>
            <version>${org.mapstruct.version}</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>
    ...
    

    基本映射

    场景:将一个对象映射成另外一种类型的对象,如将DTO映射成VO
    定义一个映射接口类CarMapper

    @Mapper
    public interface CarMapper {
        CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
    
        @Mapping(source = "make", target = "manufacturer")
        @Mapping(source = "numberOfSeats", target = "seatCount")
        CarDto carToCarDto(Car car);
    	
        @Mapping(source = "name", target = "fullName")
        PersonDto personToPersonDto(Person person);
    }
    

    代码中使用CarDTO carDTO = CarMapper.INSTANCE.carToCarDto(car)进行类型抓换(下同)。
    在生成的方法实现中,源类型(例如Car)的所有可读属性都将被复制到目标类型(例如CarDto)的相应属性中:
    当一个属性与其目标实体对应的名称相同时,它将被隐式映射。
    当属性在目标实体中具有不同的名称时,可以通过@Mapping注释指定其名称。

    多个属性的映射

    场景:为了将多个实体组合到一个数据传输对象中。

    @Mapper
    public interface AddressMapper {
    	AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);
    	
        @Mapping(source = "person.description", target = "description")
        @Mapping(source = "address.houseNo", target = "houseNumber")
        DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
    }
    

    直接引用源参数的映射方法

    @Mapper
    public interface AddressMapper {
    	AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);
    	
        @Mapping(source = "person.description", target = "description")
        @Mapping(source = "address.houseNo", target = "houseNumber")
        DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
    }
    

    更新现有的Bean实例

    场景:将一个Bean的属性更新到另外一个bean的同名属性

    @Mapper
    public interface CarMapper {
    	CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
    	
        void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
    }
    

    继承反转配置

    场景:已经存在一个Car映射成CarDTO的方法,现在需要将CarDTO映射成Car。

    @Mapper
    public interface CarMapper {
    	CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
    	   
        @Mapping(source = "make", target = "manufacturer")
        @Mapping(source = "numberOfSeats", target = "seatCount")
        CarDto carToCarDto(Car car);
    
    	@InheritInverseConfiguration(name = "carToCarDto")
    	Car carDTOTocar(CarDto carDto);
    }
    

    隐式类型转换

    在许多情况下,MapStruct会自动处理类型转换。例如,如果一个属性int在源Bean中是类型但String在目标Bean中是类型,则生成的代码将分别通过分别调用String#valueOf(int)和来透明地执行转换Integer#parseInt(String)。
    当前,以下转换将自动应用:
    之间的所有Java基本数据类型及其相应的包装类型,例如之间int和Integer,boolean和Boolean等。之间的所有Java基本类型和包装类之间,例如int和long或byte和Integer。
    从较大的数据类型转换为较小的数据类型(例如从long到int)可能会导致值或精度损失。在Mapper和MapperConfig注释有一个方法typeConversionPolicy来控制警告/错误。由于向后兼容的原因,默认值为“ ReportingPolicy.IGNORE”。
    所有Java基本类型之间(包括其包装)和String之间,例如int和String或Boolean和String。java.text.DecimalFormat可以指定理解的格式字符串。
    从int到String的转换

    @Mapper
    public interface CarMapper {
    
        @Mapping(source = "price", numberFormat = "$#.00")
        CarDto carToCarDto(Car car);
    
        @IterableMapping(numberFormat = "$#.00")
        List<String> prices(List<Integer> prices);
    }
    

    从BigDecimal到String的转换

    @Mapper
    public interface CarMapper {
    
        @Mapping(source = "power", numberFormat = "#.##E0")
        CarDto carToCarDto(Car car);
    
    }
    

    从日期到字符串的转换

    @Mapper
    public interface CarMapper {
    
        @Mapping(source = "manufacturingDate", dateFormat = "dd.MM.yyyy")
        CarDto carToCarDto(Car car);
    
        @IterableMapping(dateFormat = "dd.MM.yyyy")
        List<String> stringListToDateList(List<Date> dates);
    }
    

    更多映射见@Mapping注解

    控制嵌套bean映射

    场景:对象内部存在多重嵌套。

    @Mapper
    public interface FishTankMapper {
    
        @Mapping(target = "fish.kind", source = "fish.type")
        @Mapping(target = "fish.name", ignore = true)
        @Mapping(target = "ornament", source = "interior.ornament")
        @Mapping(target = "material.materialType", source = "material")
        @Mapping(target = "quality.report.organisation.name", source = "quality.report.organisationName")
        FishTankDto map( FishTank source );
    }
    

    映射表达式

    场景:target属性需要通过计算得到。这个功能很强大,可以适用很多场景,比如将Integer类转换成枚举类,单位换算等等。

    @Mappings({
                @Mapping(target = "extensionInsuranceAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getExtensionInsuranceAmount()))"),
                @Mapping(target = "insuranceAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getInsuranceAmount()))"),
                @Mapping(target = "purchaseTax", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getPurchaseTax()))"),
                @Mapping(target = "decorationAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getDecorationAmount()))"),
                @Mapping(target = "boutiqueAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getBoutiqueAmount()))"),
                @Mapping(target = "maintainPackageAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getMaintainPackageAmount()))"),
                @Mapping(target = "repairAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getRepairAmount()))"),
                @Mapping(target = "renewalFundAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getRenewalFundAmount()))")
        })
        AdditionalDetailDTO map(AdditionalDetailVO vo);
    

    映射集合

    场景:将一个集合映射成另外一个集合。

    @Mapper
    public interface CarMapper {
    
        Set<String> integerSetToStringSet(Set<Integer> integers);
    
        List<CarDto> carsToCarDtos(List<Car> cars);
    
        CarDto carToCarDto(Car car);
    }
    

    映射map

    场景:将一个Map对象映射成另外一个Map对象

    public interface SourceTargetMapper {
    
        @MapMapping(valueDateFormat = "dd.MM.yyyy")
        Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
    }
    

    映射枚举类型

    可以在@ValueMapping注释的帮助下将源枚举中的常量映射到具有其他名称的常量。来自源枚举的多个常量可以映射到目标类型中的相同常量。

    @Mapper
    public interface OrderMapper {
    
        OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class );
    
        @ValueMappings({
            @ValueMapping(source = "EXTRA", target = "SPECIAL"),
            @ValueMapping(source = "STANDARD", target = "DEFAULT"),
            @ValueMapping(source = "NORMAL", target = "DEFAULT")
        })
        ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
    }
    

    默认值和常量

    分别可以通过@Mapping的defaultValue和constant属性指定,当source对象的属性值为null时,如果有指定defaultValue将注入defaultValue的设定的值。constant属性通用用于给target属性注入常量值。

    @Mapper(uses = StringListMapper.class)
    public interface SourceTargetMapper {
    
        SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
    
        @Mapping(target = "stringProperty", source = "stringProp", defaultValue = "undefined")
        @Mapping(target = "longProperty", source = "longProp", defaultValue = "-1")
        @Mapping(target = "stringConstant", constant = "Constant Value")
        @Mapping(target = "integerConstant", constant = "14")
        @Mapping(target = "longWrapperConstant", constant = "3001")
        @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014")
        @Mapping(target = "stringListConstants", constant = "jack-jill-tom")
        Target sourceToTarget(Source s);
    }
    

    如果为s.getStringProp() == null,则将target属性stringProperty设置为"undefined"而不是应用来自s.getStringProp()的值。如果为s.getLongProperty() == null,则目标属性longProperty将设置为-1。将String "Constant Value"设置为目标属性stringConstant。该值"3001"被类型转换为 targe类的longWrapperConstant属性。该常量"jack-jill-tom"将破折号分隔的列表映射到List。

    展开全文
  • MapStruct是一种类型安全的bean映射类生成java注释处理器。 我们要做的就是定义一个映射器接口,声明任何必需的映射方法。在编译的过程中,MapStruct会生成此接口的实现。该实现使用纯java方法调用的源和目标对象...

    MapStruct是一种类型安全的bean映射类生成java注释处理器。
    我们要做的就是定义一个映射器接口,声明任何必需的映射方法。在编译的过程中,MapStruct会生成此接口的实现。该实现使用纯java方法调用的源和目标对象之间的映射,MapStruct节省了时间,通过生成代码完成繁琐和容易出错的代码逻辑。下面我们来揭开它的神秘面纱

    本章目标

    基于SpringBoot平台完成MapStruct映射框架的集成。

    SpringBoot 企业级核心技术学习专题

    专题 专题名称 专题描述
    001 Spring Boot 核心技术 讲解SpringBoot一些企业级层面的核心组件
    002 Spring Boot 核心技术章节源码 Spring Boot 核心技术简书每一篇文章码云对应源码
    003 Spring Cloud 核心技术 对Spring Cloud核心技术全面讲解
    004 Spring Cloud 核心技术章节源码 Spring Cloud 核心技术简书每一篇文章对应源码
    005 QueryDSL 核心技术 全面讲解QueryDSL核心技术以及基于SpringBoot整合SpringDataJPA
    006 SpringDataJPA 核心技术 全面讲解SpringDataJPA核心技术

    构建项目

    我们使用idea开发工具创建一个SpringBoot项目,添加相应的依赖,pom.xml配置文件如下所示:

    ...省略部分代码
    <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.6.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
            <org.mapstruct.version>1.2.0.CR1</org.mapstruct.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <!--<scope>provided</scope>-->
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!--mapStruct依赖-->
            <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>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.inject</groupId>
                <artifactId>javax.inject</artifactId>
                <version>1</version>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.31</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
        </dependencies>
    ....省略部分代码

    集成MapStruct官方提供了两种方式,上面配置文件内我们采用的是直接添加Maven依赖,而官方文档还提供了另外一种方式,采用Maven插件形式配置,配置如下所示:

    ...引用官方文档
    ...
    <properties>
        <org.mapstruct.version>1.2.0.CR1</org.mapstruct.version>
    </properties>
    ...
    <dependencies>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>${org.mapstruct.version}</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>
    ...

    我个人比较喜欢采用第一种方式,不需要配置过多的插件,依赖方式比较方便。
    接下来我们开始配置下数据库连接信息以及简单的两张表的SpringDataJPA相关接口。

    数据库连接信息

    在resource下新创建一个application.yml文件,并添加如下数据库连接配置:

    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8
        username: root
        password: 123456
        #最大活跃数
        maxActive: 20
        #初始化数量
        initialSize: 1
        #最大连接等待超时时间
        maxWait: 60000
        #打开PSCache,并且指定每个连接PSCache的大小
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        #通过connectionProperties属性来打开mergeSql功能;慢SQL记录
        #connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
        minIdle: 1
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: select 1 from dual
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        #配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙
        filters: stat, wall, log4j
      jpa:
        properties:
          hibernate:
            show_sql: true
            format_sql: true

    有关SpringDataJPA相关的学习请访问第三章:SpringBoot使用SpringDataJPA完成CRUD,我们在数据库内创建两张表信息分别是商品基本信息表、商品类型表。
    两张表有相应的关联,我们在不采用连接查询的方式模拟使用MapStruct,表信息如下所示:

    --商品类型信息表
    CREATE TABLE `good_types` (
      `tgt_id` int(11) NOT NULL AUTO_INCREMENT,
      `tgt_name` varchar(30) DEFAULT NULL,
      `tgt_is_show` int(1) DEFAULT NULL,
      `tgt_order` int(255) DEFAULT NULL,
      PRIMARY KEY (`tgt_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
    
    --商品基本信息表
    CREATE TABLE `good_infos` (
      `tg_id` int(11) NOT NULL AUTO_INCREMENT,
      `tg_type_id` int(11) DEFAULT NULL,
      `tg_title` varchar(30) DEFAULT NULL,
      `tg_price` decimal(8,2) DEFAULT NULL,
      `tg_order` int(2) DEFAULT NULL,
      PRIMARY KEY (`tg_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
    
    INSERT INTO `good_types` VALUES ('1', '青菜', '1', '1');
    INSERT INTO `good_infos` VALUES ('1', '1', '芹菜', '12.40', '1');

    下面我们根据这两张表创建对应的实体类。

    商品类型实体

    package com.yuqiyu.chapter30.bean;
    
    import lombok.Data;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    /**
     * ========================
     * Created with IntelliJ IDEA.
     * User:恒宇少年
     * Date:2017/8/20
     * Time:11:17
     * 码云:http://git.oschina.net/jnyqy
     * ========================
     */
    @Entity
    @Table(name = "good_types")
    @Data
    public class GoodTypeBean
    {
        @Id
        @Column(name = "tgt_id")
        private Long id;
    
        @Column(name = "tgt_name")
        private String name;
        @Column(name = "tgt_is_show")
        private int show;
        @Column(name = "tgt_order")
        private int order;
    
    }
    

    商品基本信息实体

    package com.yuqiyu.chapter30.bean;
    
    import lombok.Data;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    /**
     * ========================
     * Created with IntelliJ IDEA.
     * User:恒宇少年
     * Date:2017/8/20
     * Time:11:16
     * 码云:http://git.oschina.net/jnyqy
     * ========================
     */
    @Entity
    @Table(name = "good_infos")
    @Data
    public class GoodInfoBean
    {
        @Id
        @Column(name = "tg_id")
        private Long id;
        @Column(name = "tg_title")
        private String title;
        @Column(name = "tg_price")
        private double price;
        @Column(name = "tg_order")
        private int order;
        @Column(name = "tg_type_id")
        private Long typeId;
    }

    接下来我们继续创建相关的JPA。

    商品类型JPA

    package com.yuqiyu.chapter30.jpa;
    
    import com.yuqiyu.chapter30.bean.GoodTypeBean;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    /**
     * ========================
     * Created with IntelliJ IDEA.
     * User:恒宇少年
     * Date:2017/8/20
     * Time:11:24
     * 码云:http://git.oschina.net/jnyqy
     * ========================
     */
    public interface GoodTypeJPA
        extends JpaRepository<GoodTypeBean,Long>
    {
    }
    

    商品信息JPA

    package com.yuqiyu.chapter30.jpa;
    
    import com.yuqiyu.chapter30.bean.GoodInfoBean;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    /**
     * ========================
     * Created with IntelliJ IDEA.
     * User:恒宇少年
     * Date:2017/8/20
     * Time:11:23
     * 码云:http://git.oschina.net/jnyqy
     * ========================
     */
    public interface GoodInfoJPA
        extends JpaRepository<GoodInfoBean,Long>
    {
    
    }

    配置MapStruct

    到目前为止我们的准备工作差不多完成了,下面我们开始配置使用MapStruct。我们的最终目的是为了返回一个自定义的DTO实体,那么我们就先来创建这个DTO,DTO的代码如下所示:

    package com.yuqiyu.chapter30.dto;
    
    import lombok.Data;
    
    /**
     * 转换Dto
     * ========================
     * Created with IntelliJ IDEA.
     * User:恒宇少年
     * Date:2017/8/20
     * Time:11:25
     * 码云:http://git.oschina.net/jnyqy
     * ========================
     */
    @Data
    public class GoodInfoDTO
    {
        //商品编号
        private String goodId;
        //商品名称
        private String goodName;
        //商品价格
        private double goodPrice;
        //类型名称
        private String typeName;
    }

    可以看到GoodInfoDTO实体内集成了商品信息、商品类型两张表内的数据,对应查询出信息后,我们需要使用MapStruct自动映射到GoodInfoDTO。

    创建Mapper

    Mapper这个定义一般是被广泛应用到MyBatis半自动化ORM框架上,而这里的Mapper跟Mybatis没有关系。下面我们先来看下代码,如下所示:

    package com.yuqiyu.chapter30.mapper;
    
    import com.yuqiyu.chapter30.bean.GoodInfoBean;
    import com.yuqiyu.chapter30.bean.GoodTypeBean;
    import com.yuqiyu.chapter30.dto.GoodInfoDTO;
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.Mappings;
    
    /**
     * 配置映射
     * ========================
     * Created with IntelliJ IDEA.
     * User:恒宇少年
     * Date:2017/8/20
     * Time:11:26
     * 码云:http://git.oschina.net/jnyqy
     * ========================
     */
    @Mapper(componentModel = "spring")
    //@Mapper
    public interface GoodInfoMapper
    {
        //public static GoodInfoMapper MAPPER = Mappers.getMapper(GoodInfoMapper.class);
    
        @Mappings({
                @Mapping(source = "type.name",target = "typeName"),
                @Mapping(source = "good.id",target = "goodId"),
                @Mapping(source = "good.title",target = "goodName"),
                @Mapping(source = "good.price",target = "goodPrice")
        })
        public GoodInfoDTO from(GoodInfoBean good, GoodTypeBean type);
    }

    可以看到GoodInfoMapper是一个接口的形式存在的,当然也可以是一个抽象类,如果你需要在转换的时候才用个性化的定制的时候可以采用抽象类的方式,相应的代码配置官方文档已经声明。
    @Mapper注解是用于标注接口、抽象类是被MapStruct自动映射的标识,只有存在该注解才会将内部的接口方法自动实现。
    MapStruct为我们提供了多种的获取Mapper的方式,比较常用的两种分别是

    默认配置

    默认配置,我们不需要做过多的配置内容,获取Mapper的方式就是采用Mappers通过动态工厂内部反射机制完成Mapper实现类的获取。
    默认方式获取Mapper如下所示:

    //Mapper接口内部定义
    public static GoodInfoMapper MAPPER = Mappers.getMapper(GoodInfoMapper.class);
    
    //外部调用
    GoodInfoMapper.MAPPER.from(goodBean,goodTypeBean);
    Spring方式配置

    Spring方式我们需要在@Mapper注解内添加componentModel属性值,配置后在外部可以采用@Autowired方式注入Mapper实现类完成映射方法调用。
    Spring方式获取Mapper如下所示:

    //注解配置
    @Mapper(componentModel = "spring")
    
    //注入Mapper实现类
    @Autowired
    private GoodInfoMapper goodInfoMapper;
    
    //调用
    goodInfoMapper.from(goodBean,goodTypeBean);
    @Mappings & @Mapping

    Mapper接口定义方法上面声明了一系列的注解映射@Mapping以及@Mappings,那么这两个注解是用来干什么工作的呢?
    @Mapping注解我们用到了两个属性,分别是sourcetarget

    source代表的是映射接口方法内的参数名称,如果是基本类型的参数,参数名可以直接作为source的内容,如果是实体类型,则可以采用实体参数名.字段名的方式作为source的内容,配置如上面GoodInfoMapper内容所示。

    target代表的是映射到方法方法值内的字段名称,配置如上面GoodInfoMapper所示。

    查看Mapper实现

    下面我们执行maven compile命令,到target/generated-sources/annotations目录下查看对应Mapper实现类,实现类代码如下所示:

    package com.yuqiyu.chapter30.mapper;
    
    import com.yuqiyu.chapter30.bean.GoodInfoBean;
    import com.yuqiyu.chapter30.bean.GoodTypeBean;
    import com.yuqiyu.chapter30.dto.GoodInfoDTO;
    import javax.annotation.Generated;
    import org.springframework.stereotype.Component;
    
    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2017-08-20T12:52:52+0800",
        comments = "version: 1.2.0.CR1, compiler: javac, environment: Java 1.8.0_111 (Oracle Corporation)"
    )
    @Component
    public class GoodInfoMapperImpl implements GoodInfoMapper {
    
        @Override
        public GoodInfoDTO from(GoodInfoBean good, GoodTypeBean type) {
            if ( good == null && type == null ) {
                return null;
            }
    
            GoodInfoDTO goodInfoDTO = new GoodInfoDTO();
    
            if ( good != null ) {
                if ( good.getId() != null ) {
                    goodInfoDTO.setGoodId( String.valueOf( good.getId() ) );
                }
                goodInfoDTO.setGoodName( good.getTitle() );
                goodInfoDTO.setGoodPrice( good.getPrice() );
            }
            if ( type != null ) {
                goodInfoDTO.setTypeName( type.getName() );
            }
    
            return goodInfoDTO;
        }
    }
    

    MapStruct根据我们配置的@Mapping注解自动将source实体内的字段进行了调用target实体内字段的setXxx方法赋值,并且做出了一切参数验证。
    我们采用了Spring方式获取Mapper,在自动生成的实现类上MapStruct为我们自动添加了@ComponentSpring声明式注入注解配置。

    运行测试

    下面我们来创建一个测试的Controller,用于访问具体请求地址时查询出商品的基本信息以及商品的类型后调用GoodInfoMapper.from(xxx,xxx)方法完成返回GoodInfoDTO实例。Controller代码实现如下所示:

    package com.yuqiyu.chapter30.controller;
    
    import com.yuqiyu.chapter30.bean.GoodInfoBean;
    import com.yuqiyu.chapter30.bean.GoodTypeBean;
    import com.yuqiyu.chapter30.dto.GoodInfoDTO;
    import com.yuqiyu.chapter30.jpa.GoodInfoJPA;
    import com.yuqiyu.chapter30.jpa.GoodTypeJPA;
    import com.yuqiyu.chapter30.mapper.GoodInfoMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 测试控制器
     * ========================
     * Created with IntelliJ IDEA.
     * User:恒宇少年
     * Date:2017/8/20
     * Time:12:24
     * 码云:http://git.oschina.net/jnyqy
     * ========================
     */
    @RestController
    public class GoodInfoController
    {
        /**
         * 注入商品基本信息jpa
         */
        @Autowired
        private GoodInfoJPA goodInfoJPA;
        /**
         * 注入商品类型jpa
         */
        @Autowired
        private GoodTypeJPA goodTypeJPA;
        /**
         * 注入mapStruct转换Mapper
         */
        @Autowired
        private GoodInfoMapper goodInfoMapper;
    
        /**
         * 查询商品详情
         * @param id
         * @return
         */
        @RequestMapping(value = "/detail/{id}")
        public GoodInfoDTO detail(@PathVariable("id") Long id)
        {
            //查询商品基本信息
            GoodInfoBean goodInfoBean = goodInfoJPA.findOne(id);
            //查询商品类型基本信息
            GoodTypeBean typeBean = goodTypeJPA.findOne(goodInfoBean.getTypeId());
            //返回转换dto
            return goodInfoMapper.from(goodInfoBean,typeBean);
        }
    }

    在Controller内我们注入了GoodInfoJPAGoodTypeJPA以及GoodInfoMapper,在查询商品详情方法时做出了映射处理。接下来我们启动项目访问地址http://127.0.0.1:8080/detail/1查看界面输出效果,如下所示:

    {
    goodId: "1",
    goodName: "芹菜",
    goodPrice: 12.4,
    typeName: "青菜"
    }

    可以看到界面输出了GoodInfoDTO内的所有字段内容,并且通过from方法将对应配置的target字段赋值。

    总结

    本章主要讲述了基于SpringBoot开发框架上集成MapStruct自动映射框架,完成模拟多表获取数据后将某一些字段通过@Mapping配置自动映射到DTO实体实例指定的字段内。
    MapStruct官方文档地址:http://mapstruct.org/documentation/dev/reference/html/

    本章代码已经上传到码云:
    SpringBoot配套源码地址:https://gitee.com/hengboy/spring-boot-chapter
    SpringCloud配套源码地址:https://gitee.com/hengboy/spring-cloud-chapter
    SpringBoot相关系列文章请访问:目录:SpringBoot学习目录
    QueryDSL相关系列文章请访问:QueryDSL通用查询框架学习目录
    SpringDataJPA相关系列文章请访问:目录:SpringDataJPA学习目录
    感谢阅读!

    更多干货文章扫码关注微信公众号

    扫码关注 - 专注分享

    加入知识星球,恒宇少年带你走以后的技术道路!!!

    微信扫码加入

    展开全文
  • 推荐一个 Java 实体映射工具 MapStruct

    万次阅读 多人点赞 2018-06-18 21:21:21
    声明: 1、DO(业务实体对象),DTO(数据传输对象)。 2、我的代码中用到了 Lombok ,不了解的可以自行了解一下,了解的忽略这条就好。 在一个成熟的工程中,尤其是现在的分布式系统中,应用与应用之间,还有单独...
  • MapStruct的使用

    千次阅读 2019-09-05 17:28:20
    MapStruct 代替BeanUtil 和ModelMapper:https://blog.csdn.net/paincupid/article/details/71247255 推荐一个 Java 实体映射工具 MapStruct:https://blog.csdn.net/zhige_me/article/details/80699784 ...
  • MapStruct-实体映射处理器

    千次阅读 2019-01-29 11:38:15
    MapStruct-实体映射处理器 对象映射大体分为两种: 运行期:反射调用set/get 或者是直接对成员变量赋值 。 * 该方式通过invoke执行赋值,实现时一般会采用beanutil, Javassist等开源库。这类的代表:Dozer,...
  • MapStruct 详解

    万次阅读 2018-10-27 13:18:07
    GitHub 访问地址 : https://github.com/mapstruct/mapstruct/ 使用例子 : https://github.com/mapstruct/mapstruct-examples   MapStrcut与其它工具对比以及使用说明 : ...
  • mapStruct学习笔记

    2020-10-27 15:19:41
    了解mapStruct MapStruct是一个代码生成器,它基于约定优先于配置的方法,极大地简化了javabean类型之间映射的实现。 生成的映射代码使用纯方法调用,因此速度快、类型安全且易于理解。 多层应用程序通常需要在不同...
  • mapstruct使用的正确姿势

    千次阅读 多人点赞 2020-04-22 09:31:12
    我们都知道,随着一个工程的越来越成熟,模块划分会越来越细,其中实体类一般存于 domain 之中,但 domain 工程最好不要被其他工程依赖,所以其他工程想...所以阿淼今天就要给大家安利一款叫 mapstruct 的插件,它就...
  • MapStruct

    2018-11-24 23:10:13
    一般java web开发的程序员,都有一个共同的体会,接受请求参数都会使用一个vo类,这个vo类里封装了所有需要接受的参数,然后对参数进行业务逻辑处理,处理完后会持久化处理,通常每个业务表都会对应一个bean类,然后...
  • mapstruct 之 类型转换

    千次阅读 2019-09-18 14:40:06
    参考链接:mapstruct的基本使用 背景介绍 是不是有时候发现明明source和target不是同一个类型,但是却转换成功了,这是因为mapstruct有一套自己的类型转换机制 类型转换的流程 首先尝试自动进行类型转换 若是无法...
  • Springboot集成mapstruct

    千次阅读 2019-10-07 22:28:44
    一、什么是mapstruct MapStruct是一个代码生成器的工具类,简化了不同的Java Bean之间映射的处理,所以映射指的就是从一个实体变化成一个实体。在实际项目中,我们经常会将PO转DTO、DTO转PO等一些实体间的转换。在...
  • Mapstruct

    2018-02-07 20:10:58
    给自己提个醒, 有的时候需要不同类型的转化, 此时自定义转化类的时候,需要加上注解@component注解 具体的用法,大家自行百度,很简单。
  • mapstruct 对象映射详解

    千次阅读 2019-12-07 07:06:32
    1、@Mapper项目编译时会生产对应实现类 2、@Mapping 用来指定属性映射的,如果两个对象的属性名相同是可以省略
  • MapStruct 对象值拷贝

    2020-05-31 00:51:11
    介绍 前提 在Java 项目开发中,存在需要连个不同类的对象的转化的情况, 例如VO与DO 的中同一逻辑对象的值转换。 @AllArgsConstructor @Data public class UserVo { private Long id; private String username;...
  • MapStruct 1.1.0.Final中文参考指南

    千次阅读 2018-10-16 15:40:57
     这是MapStruct的参考文档,它是一个用于生成类型安全、高性能和无依赖的bean映射代码的注解处理器。本文档涵盖了MapStruct提供的所有功能。如果这个指南没有回答你所有的问题,只要加入MapStruct谷歌论坛就可以...
  • Mapstruct原理

    千次阅读 2019-05-21 11:17:11
    一、Mapstruct简介 MapStruct是用于生成类型安全的bean映射类的Java注解处理器。 你所要做的就是定义一个映射器接口,声明任何需要映射的方法。在编译过程中,MapStruct将生成该接口的实现。此实现使用纯Java的...
  • 安装 目录 安装 分发包 ...您可以从GitHub获取包含MapStruct的二进制文件,源代码和API文档的分发包。 Apache Maven 如果使用Maven构建项目,则将以下内容添加到pom.xml中以使用MapStruct: ...
  • mapStruct 之 基本使用

    2019-09-18 14:30:27
    简介 mapstrct是一个很好注释处理的框架,解决...mapstrut一共两个主要的包,org.mapstruct.mapstruct包含里面常用的注释,org.mapstruct.mapstruct-processor处理注释的实现。 官方文档 文档传送门 maven引入 <...
1 2 3 4 5 ... 20
收藏数 264,731
精华内容 105,892
关键字:

mapstruct