①. MapStruct是一款基于Java注解的对象属性映射工具,使用的时候我们只要在接口中定义好对象属性映射规则,它就能自动生成映射实现类,不使用反射,性能优秀,能实现各种复杂映射
②. 在平时CRUD的工作中,经常需要做PO、VO、DTO之间的转换。简单的对象转换,使用BeanUtils基本上是够了,但是复杂的转换,如果使用它的话又得写一堆Getter、Setter方法了。BeanUtils 就是一个大老粗,只能同属性映射,或者在属性相同的情况下,允许被映射的对象属性少;但当遇到被映射的属性数据类型被修改或者被映射的字段名被修改,则会导致映射失败
③. IDEA下载mapstruct support插件
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 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 ListproductList; //商品集合 } @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; }
@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 ListproductVoList; //商品集合 }
@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; }
@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); }
@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 ListproductList = 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 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); }
@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); }
@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; }
@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; }
@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); }
result = xxxList.stream() .map(XXX.INSTANCE::convertNew).collect(Collectors.toList());