相关推荐recommended
MapStruct
作者:mmseoamin日期:2024-01-19

文章目录

  • ①. 什么是MapStruct?
  • ②. 如何使用MapStruct?
  • ③. 子集和映射
  • ④. 合并映射
  • ⑤. Spring依赖注入
  • ⑥. 常量、默认值和表达式
  • ⑦. 自定义切面处理

    ①. 什么是MapStruct?

    • ①. MapStruct是一款基于Java注解的对象属性映射工具,使用的时候我们只要在接口中定义好对象属性映射规则,它就能自动生成映射实现类,不使用反射,性能优秀,能实现各种复杂映射

    • ②. 在平时CRUD的工作中,经常需要做PO、VO、DTO之间的转换。简单的对象转换,使用BeanUtils基本上是够了,但是复杂的转换,如果使用它的话又得写一堆Getter、Setter方法了。BeanUtils 就是一个大老粗,只能同属性映射,或者在属性相同的情况下,允许被映射的对象属性少;但当遇到被映射的属性数据类型被修改或者被映射的字段名被修改,则会导致映射失败

    • ③. IDEA下载mapstruct support插件

      MapStruct,在这里插入图片描述,第1张

      ②. 如何使用MapStruct?

      • ①. 引入MapStruct依赖
            
                1.18.12
                1.4.2.Final
            
        	
        	
        		org.mapstruct
        		mapstruct
        		${mapstruct.version}
        	
        	
        		org.mapstruct
        		mapstruct-processor
        		${mapstruct.version}
        		compile
        	
        		
            
            
                
                    
                    
                        org.apache.maven.plugins
                        maven-compiler-plugin
                        
                            1.8
                            1.8
                            
                                
                                    org.mapstruct
                                    mapstruct-processor
                                    1.4.2.Final
                                
                            
                        
                    
                
            
        
        • ②. 创建我们所需要的案例实体类
          @Data
          @NoArgsConstructor
          @AllArgsConstructor
          @Accessors(chain = true)
          public class User {
              private Integer id ;//用户id
              private String userName;//用户名
              private String password; //密码
              private Date birthday;//生日
              private String tel;//电话号码
              private String email; //邮箱
              private String idCardNo;//身份证号
              private String icon; //头像
              private Integer gender;//性别
          }
          
          @Data
          @NoArgsConstructor
          @AllArgsConstructor
          @Accessors(chain = true)
          @EqualsAndHashCode(callSuper = false)
          public class UserVo {
              private Long id ;//用户id
              private String userName;//用户名
              private String password; //密码
          	// 与User对象不同的类型
              private String birthday;//生日
              //与User不同的名称
              private String telNumber;//电话号码
              private String email; //邮箱
              private String idCardNo;//身份证号
              private String icon; //头像
              private Integer gender;//性别
          }
          
          • ③. 创建映射接口(目的:实现同名同类型属性、不同名称属性、不同类型属性的映射)
            /**
             * unmappedTargetPolicy:
             * 目标属性不存在时的处理策略,可选值有:IGNORE默认值、WARN和ERROR。
             * IGNORE默认值:忽略未映射的源属性
             * WARN:任何未映射的源属性都将在生成时引起警告,基于javax.tools.Diagnostic.Kind.WARNING的警告。
             * ERROR:任何未映射的源属性都将导致映射代码生成失败。
             *
             */
            @Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
            public interface UserMapper {
                UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
                @Mapping(source = "tel",target = "telNumber")
                @Mapping(source = "birthday",target = "birthday",dateFormat = "yyyy-MM-dd")
                UserVo convertToVo(User user);
            }
            
            • ④. 案例演示
              @RestController
              @RequestMapping("/testController")
              @Slf4j
              public class TestController {
                  @GetMapping("/mapStructToVo")
                  public String mapStructToVo() {
                      User user = new User();
                      user.setId(1).setEmail("84519548@qq.com").setUserName("tang").setBirthday(new Date()).setTel("18774149799");
                      UserVo userVo = UserMapper.INSTANCE.convertToVo(user);
                      // {"birthday":"2023-10-07","email":"84519548@qq.com","id":1,"telNumber":"18774149799","userName":"tang"}
                      System.out.println(JSON.toJSONString(userVo));
                      return JSON.toJSONString(userVo);
                  }
              }
              

              ③. 子集和映射

              • ①. MapStruct对于对象中包含子对象也需要转换的情况也是有所支持的

              • ②. 有一个订单PO对象Order,嵌套有User和Product对象

                @Data
                @EqualsAndHashCode(callSuper = false)
                public class Order {
                    private Long id;
                    private String orderNo;//订单号
                    private Date createTime;
                    private String receiverAddress; //收货地址
                    private User user;//订单所属的用户
                    private List productList; //商品集合
                }
                @Data
                @Accessors(chain = true)
                @EqualsAndHashCode(callSuper = false)
                public class Product {
                    private Long id;
                    private String productSn;
                    private String name;
                    private String subTitle;
                    private String brandName;
                    private BigDecimal price;
                    private Integer count;//商品数量
                    private Date createTime;
                }
                
                • ③. 我们需要转换为OrderDo对象,OrderDo中包含UserVo和ProductVo两个子对象同样需要转换;
                  @Data
                  @EqualsAndHashCode(callSuper = false)
                  public class OrderVo {
                      private Long id;
                      private String orderNo; //订单号
                      private Date createTime;
                      private String receiverAddress; //收货地址
                      //子对象映射Dto
                      private UserVo userVo;//订单所属的用户
                      //子对象数组映射Dto
                      private List productVoList; //商品集合
                  }
                  
                  @Data
                  @EqualsAndHashCode(callSuper = false)
                  public class ProductVo {
                      //使用常量
                      private Long id;
                      //使用表达式生成属性
                      private String productSn;
                      private String name;
                      private String subTitle;
                      private String brandName;
                      private BigDecimal price;
                      //使用默认值
                      private Integer number;//商品数量
                      private Date createTime;
                  }
                  
                  • ④. 使用uses将子对象的转换Mapper注入进来,然后通过@Mapping设置好属性映射规则即可
                    @Mapper(uses = {UserMapper.class,ProductMapper.class})
                    public interface OrderMapper {
                        OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class);
                        @Mapping(source = "user",target = "UserVo")
                        @Mapping(source = "productList",target = "productVoList")
                        OrderVo convertToVo(Order order);
                    }
                    
                    @Mapper(imports = {UUID.class})
                    public interface ProductMapper {
                        ProductMapper INSTANCE = Mappers.getMapper(ProductMapper.class);
                        @Mapping(target = "id",constant = "-1L")
                        @Mapping(source = "count",target = "number",defaultValue = "1")
                        @Mapping(target = "productSn",expression = "java(UUID.randomUUID().toString())")
                        ProductVo convertToVo(Product product);
                    }
                    
                    • ⑤. 直接通过Mapper中的INSTANCE实例调用转换方法toDto;
                          @GetMapping("/mapStructToSubVo")
                          public String  mapStructToSubVo() {
                              //创建一个user对象
                              User user = new User();
                              user.setId(1).setEmail("845195485@qq.com").setUserName("tang")
                                      .setBirthday(new Date()).setTel("18774149799");
                              //创建productList
                              List productList = new ArrayList<>();
                              productList.add(new Product().setCount(3).setName("test-nameA"));
                              productList.add(new Product().setCount(7).setName("test-nameB"));
                              Order order = new Order();
                              order.setUser(user).setProductList(productList);
                              OrderVo orderVo = OrderMapper.INSTANCE.convertToVo(order);
                              // {"productVoList":[{"id":-1,"name":"test-nameA","number":3,"productSn":"d7cacdd0-4a13-46b1-a76b-fba7607d68ea"},{"id":-1,"name":"test-nameB","number":7,"productSn":"18f7c91e-c5f1-4bb6-8ae3-6e1e5847f03c"}],"userVo":{"birthday":"2023-10-12","email":"845195485@qq.com","id":1,"telNumber":"18774149799","userName":"tang"}}
                              System.out.println(JSON.toJSONString(orderVo));
                              return JSON.toJSONString(orderVo);
                          }
                      

                      ④. 合并映射

                      • ①. MapStruct支持把多个对象属性映射到一个对象中去

                      • ②. 把User和Order的部分属性映射到UserOrderDto中去

                        @Data
                        public class UserOrderVo {
                            private Long id ;//用户id
                            private String userName;//用户名
                            private String password; //密码
                            //与PO类型不同的属性
                            private String birthday;//生日
                            //与PO名称不同的属性
                            private String telNumber;//电话号码
                            private String email;
                            private String idCardNo;//身份证号
                            private String icon; //头像
                            private Integer gender;//性别
                            private String orderNo; //订单号
                            private String receiverAddress; //用户收货地址
                        }
                        
                        • ③. 在Mapper中添加toUserOrderVo方法,这里需要注意的是由于参数中具有两个属性,需要通过参数名称.属性的名称来指定source来防止冲突这两个参数中都有id属性
                          @Mapper
                          public interface UserMapper {
                              UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
                              @Mapping(source = "user.tel",target = "telNumber")
                              @Mapping(source = "user.birthday",target = "birthday",dateFormat = "yyyy-MM-dd")
                              @Mapping(source = "user.id",target = "id")
                              @Mapping(source = "order.orderNo", target = "orderNo")
                              @Mapping(source = "order.receiverAddress", target = "receiverAddress")
                              UserOrderVo toUserOrderVo(User user, Order order); 
                          }
                          
                          • ④. 测试
                                @ApiOperation(value = "组合映射")
                                @GetMapping("/compositeMapping")
                                public String compositeMapping() {
                                    //新建一个user对象
                                    User user = new User();
                                    user.setBirthday(new Date()).setTel("110");
                                    //新建一个Order对象
                                    Order order = new Order();
                                    order.setReceiverAddress("湖南长沙测试").setOrderNo("123456789");
                                    // {"birthday":"2023-10-12","orderNo":"123456789","receiverAddress":"湖南长沙测试","telNumber":"110"}
                                    UserOrderVo userOrderVo = UserMapper.INSTANCE.toUserOrderVo(user,order);
                                    System.out.println(JSON.toJSONString(userOrderVo));
                                    return JSON.toJSONString(userOrderVo);
                                }
                            

                            ⑤. Spring依赖注入

                            • ①. 想要使用依赖注入,我们只要将@Mapper注解的componentModel参数设置为spring即可,这样在生成接口实现类时,MapperStruct会为其添加@Component注解
                              @Mapper(componentModel = "spring")
                              public interface UserSpringMapper {
                                  @Mappings({
                                          @Mapping(source = "tel", target = "telNumber"),
                                          @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
                                  })
                                  UserVo convertToVo(User user);
                              }
                              
                              • ②. 测试数据
                                    @Autowired
                                    private UserSpringMapper userMapper;
                                    @GetMapping("/mapStructToVoSpring")
                                    public String mapStructToVoSpring() {
                                        User user = new User();
                                        // {"birthday":"2023-10-12","email":"845195485@qq.com","id":1,"telNumber":"18774149733","userName":"tang"}
                                        user.setId(1).setEmail("845195485@qq.com").setUserName("tang").setBirthday(new Date()).setTel("18774149733");
                                        UserVo userVo = userMapper.convertToVo(user);
                                        System.out.println(JSON.toJSONString(userVo));
                                        return JSON.toJSONString(userVo);
                                    }
                                

                                ⑥. 常量、默认值和表达式

                                • ①. 使用MapStruct映射属性时,我们可以设置属性为常量或者默认值,也可以通过Java中的方法编写表达式来自动生成属性
                                  @Data
                                  @Accessors(chain = true)
                                  public class Product {
                                      private Long id;
                                      private String productSn;
                                      private String name;
                                      private String subTitle;
                                      private String brandName;
                                      private BigDecimal price;
                                      private Integer count;//商品数量
                                      private Date createTime;
                                  }
                                  
                                  • ②. Product转换为ProductVo对象,id属性设置为常量,count设置默认值为1,productSn设置为UUID生成
                                    @Data
                                    public class ProductVo {
                                        //使用常量
                                        private Long id;
                                        //使用表达式生成属性
                                        private String productSn;
                                        private String name;
                                        private String subTitle;
                                        private String brandName;
                                        private BigDecimal price;
                                        //使用默认值
                                        private Integer number;//商品数量
                                        private Date createTime;
                                    }
                                    
                                    • ③. 创建ProductMapper接口,通过@Mapping注解中的constant、defaultValue、expression设置好映射规则;
                                      @Mapper(imports = {UUID.class})
                                      public interface ProductMapper {
                                          ProductMapper INSTANCE = Mappers.getMapper(ProductMapper.class);
                                          @Mapping(target = "id",constant = "-1L")  //给转换后的productVo的id字段设置为常量-1
                                          @Mapping(source = "count",target = "number",defaultValue = "1")
                                          @Mapping(target = "productSn",expression = "java(UUID.randomUUID().toString())")
                                          ProductVo convertToVo(Product product);
                                      }
                                      
                                      • ④. 测试数据
                                            @GetMapping("/defaultMapping")
                                            public Result defaultMapping() {
                                                Product product = new Product();
                                                product.setId(200L);
                                                product.setCount(null);
                                                ProductVo productVo = ProductMapper.INSTANCE.convertToVo(product);
                                                System.out.println(JSON.toJSONString(productVo));
                                                return Result.success(productVo);
                                            }
                                        

                                        ⑦. 自定义切面处理

                                        • ①. MapStruct也支持在映射前后做一些自定义操作,类似Spring的AOP中的切面

                                        • ②. 此时我们需要创建自定义处理方法,创建一个抽象类ProductRoundMapper,通过@BeforeMapping注解自定义映射前操作,通过@AfterMapping注解自定义映射后操作

                                          @Mapper(imports = {UUID.class},unmappedTargetPolicy = ReportingPolicy.IGNORE)
                                          public abstract class ProductRoundMapper {
                                              public static ProductRoundMapper INSTANCE = Mappers.getMapper(ProductRoundMapper.class);
                                              @Mappings({
                                                      @Mapping(target = "id",constant = "-1L"),
                                                      @Mapping(source = "count",target = "number",defaultValue = "1"),
                                                      @Mapping(target = "productSn",expression = "java(UUID.randomUUID().toString())")
                                              })
                                              public abstract ProductVo convertToVo(Product product);
                                              @BeforeMapping
                                              public void beforeMapping(Product product){
                                                  //映射前当price<0时设置为0
                                                  if(product.getPrice().compareTo(BigDecimal.ZERO)<0){
                                                      product.setPrice(BigDecimal.ZERO);
                                                  }
                                              }
                                              @AfterMapping
                                              public void afterMapping(@MappingTarget ProductVo productVo){
                                                  //映射后设置当前时间为createTime
                                                  productVo.setCreateTime(new Date());
                                              }
                                          }
                                          
                                          • ③. 测试
                                                @GetMapping("/aspectMapping")
                                                public String defaultMapping() {
                                                    Product product = new Product();
                                                    product.setId(100L);
                                                    product.setCount(null);
                                                    product.setPrice(new BigDecimal(-100) );
                                                    ProductVo productVo = ProductRoundMapper.INSTANCE.convertToVo(product);
                                                    // {"createTime":1697113274023,"id":-1,"number":1,"price":0,"productSn":"fe154c52-8808-40e1-b0a6-68b5e6437ea5"}
                                                    System.out.println(JSON.toJSONString(productVo));
                                                    return JSON.toJSONString(productVo);
                                                }