关于java:别再写满屏的-get-set-了太-Low试试-MapStruct-高级玩法

6次阅读

共计 13256 个字符,预计需要花费 34 分钟才能阅读完成。

接上篇,如果你还不晓得 MapStruct 是什么的,倡议你看下栈长之前分享的《干掉 BeanUtils!试试这款 Bean 主动映射工具,真心弱小!!》你就分明了。

上篇介绍了 MapStruct 的基本概念,以及单个对象、对象列表的映射实际,栈长看了上篇有一些留言,当然,萝卜白菜各有所爱,喜爱就用,不喜爱就不必,没必要争执,工具好不好,不肯定适宜所有人,大家开心就好。

这篇来几个高级点的映射玩法,别再写满屏的 get-set 了,太 Low!MapStruct 高级玩法,这篇栈长带你上邪道!

1、自定义映射

当咱们映射 DTO 的时候,如果某些参数的值 MapStruct 的映射配置不能满足要求,能够应用自定义办法。

新增两个 DTO 类:

UserCustomDTO 类外面蕴含了 UserExtDTO 对象。

/**
 * 微信公众号:Java 技术栈
 * @author 栈长
 */
@Data
public class UserCustomDTO {

    private String name;

    private int sex;

    private boolean married;

    private String birthday;

    private String regDate;

    private UserExtDTO userExtDTO;

    private String memo;


}
/**
 * 微信公众号:Java 技术栈
 * @author 栈长
 */
@Data
public class UserExtDTO {

    private String regSource;

    private String favorite;

    private String school;

    private int kids;

    private String memo;

}

自定义映射:

如果 UserExtDTO 对象不想应用默认的映射,能够增加一个该参数的自定义映射办法。

/**
 * 微信公众号:Java 技术栈
 * @author 栈长
 */
@Mapper(componentModel = "spring")
public interface UserCustomStruct {@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    @Mapping(target = "regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(userDO.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")
    @Mapping(source = "userExtDO", target = "userExtDTO")
    @Mapping(target = "memo", ignore = true)
    UserCustomDTO toUserCustomDTO(UserDO userDO);

    default UserExtDTO toUserExtDTO(UserExtDO userExtDO) {UserExtDTO userExtDTO = new UserExtDTO();
        userExtDTO.setKids(userExtDO.getKids());
        userExtDTO.setFavorite(userExtDO.getFavorite());

        // 笼罩这两个值
        userExtDTO.setRegSource("默认起源");
        userExtDTO.setSchool("默认学校");

        return userExtDTO;
    }

}

当映射 UserExtDTO 对象的时候,会主动调用该接口中的自定义 toUserExtDTO 办法,实现自定义映射。

来看下生成的实现类源码:

@Component
public class UserCustomStructImpl implements UserCustomStruct {public UserCustomStructImpl() { }

    public UserCustomDTO toUserCustomDTO(UserDO userDO) {if (userDO == null) {return null;} else {UserCustomDTO userCustomDTO = new UserCustomDTO();
            if (userDO.getBirthday() != null) {userCustomDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userDO.getBirthday()));
            }

            userCustomDTO.setUserExtDTO(this.toUserExtDTO(userDO.getUserExtDO()));
            userCustomDTO.setName(userDO.getName());
            userCustomDTO.setSex(userDO.getSex());
            userCustomDTO.setMarried(userDO.isMarried());
            userCustomDTO.setRegDate(DateFormatUtils.format(userDO.getRegDate(), "yyyy-MM-dd HH:mm:ss"));
            return userCustomDTO;
        }
    }
}

没错,setUserExtDTO 办法调用了 this.toUserExtDTO 自定义办法映射。

Spring Boot 根底这篇就不介绍了,系列基础教程和示例源码能够看这里:https://github.com/javastacks…

测试一下:

/**
 * 微信公众号:Java 技术栈
 * @author 栈长
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserCustomStructTest {

    @Autowired
    private UserCustomStruct userCustomStruct;

    @Test
    public void test1() {UserExtDO userExtDO = new UserExtDO();
        userExtDO.setRegSource("公众号:Java 技术栈");
        userExtDO.setFavorite("写代码");
        userExtDO.setSchool("社会大学");
        userExtDO.setKids(1);

        UserDO userDO = new UserDO();
        userDO.setName("栈长自定义办法");
        userDO.setSex(1);
        userDO.setAge(18);
        userDO.setBirthday(new Date());
        userDO.setPhone("18888888888");
        userDO.setMarried(true);
        userDO.setRegDate(new Date());
        userDO.setMemo("666");
        userDO.setUserExtDO(userExtDO);

        UserCustomDTO userCustomDTO = userCustomStruct.toUserCustomDTO(userDO);
        System.out.println("===== 自定义办法 =====");
        System.out.println(userCustomDTO);
    }
}

输入后果:

能够看到自定义办法笼罩的两个值,后果验证胜利。

2、多参数映射

之前介绍的映射办法中只有一个参数,如果有多个参数映射成一个 DTO,该怎么弄呢?

比方:有两具独自的 DO,UserDO、UserAddressDO 映射成 UserMultiDTO。

间接上代码:

/**
 * 微信公众号:Java 技术栈
 * @author 栈长
 */
@Mapper(componentModel = "spring")
public interface UserMultiStruct {@Mapping(source = "userDO.birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    @Mapping(target = "userDO.regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(user.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")
    @Mapping(source = "userAddressDO.postcode", target = "postcode")
    @Mapping(source = "userAddressDO.address", target = "address")
    @Mapping(target = "memo", ignore = true)
    UserMultiDTO toUserMultiDTO(UserDO userDO, UserAddressDO userAddressDO);

}

间接应用指定的 对象名. 属性名 模式映射即可。

来看下生成的实现类源码:

@Component
public class UserMultiStructImpl implements UserMultiStruct {public UserMultiStructImpl() { }

    public UserMultiDTO toUserMultiDTO(UserDO userDO, UserAddressDO userAddressDO) {if (userDO == null && userAddressDO == null) {return null;} else {UserMultiDTO userMultiDTO = new UserMultiDTO();
            if (userDO != null) {if (userDO.getBirthday() != null) {userMultiDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userDO.getBirthday()));
                }

                userMultiDTO.setName(userDO.getName());
                userMultiDTO.setSex(userDO.getSex());
                userMultiDTO.setMarried(userDO.isMarried());
                if (userDO.getRegDate() != null) {userMultiDTO.setRegDate((new SimpleDateFormat()).format(userDO.getRegDate()));
                }
            }

            if (userAddressDO != null) {userMultiDTO.setPostcode(userAddressDO.getPostcode());
                userMultiDTO.setAddress(userAddressDO.getAddress());
            }

            return userMultiDTO;
        }
    }
}

测试一下:

/**
 * 微信公众号:Java 技术栈
 * @author 栈长
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMultiStructTest {

    @Autowired
    private UserMultiStruct userMultiStruct;

    @Test
    public void test1() {UserDO userDO = new UserDO();
        userDO.setName("多参数映射");
        userDO.setSex(1);
        userDO.setAge(18);
        userDO.setBirthday(new Date());
        userDO.setPhone("18888888888");
        userDO.setMarried(true);
        userDO.setRegDate(new Date());
        userDO.setMemo("666");

        UserAddressDO userAddressDO = new UserAddressDO();
        userAddressDO.setProvince("广东省");
        userAddressDO.setCity("深圳市");
        userAddressDO.setPostcode("666666");
        userAddressDO.setAddress("001 号大巷 Java 技术栈公众号");
        userAddressDO.setMemo("地址信息");

        UserMultiDTO userMultiDTO = userMultiStruct.toUserMultiDTO(userDO, userAddressDO);
        System.out.println("===== 多参数映射 =====");
        System.out.println(userMultiDTO);
    }
}

输入后果:

个人信息和地址信息都输入来了,后果验证胜利。

本文实战源代码完整版曾经上传:

https://github.com/javastacks…

3、嵌套映射

如果一个 DTO 中的值都是从一个对象中的多个嵌套对象映射时,如果不想一个个写映射,指标能够用 . 示意。

间接上代码:

/** * 微信公众号:Java 技术栈 * @author 栈长 */@Mapper(componentModel = "spring")public interface UserNestedStruct {@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")    @Mapping(target = "regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(userNestedDO.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")    @Mapping(source = "userAddressDO", target = ".")    @Mapping(source = "userExtDO", target = ".")    @Mapping(source = "userExtDO.memo", target = "memo")    UserNestedDTO toUserNestedDTO(UserNestedDO userNestedDO);}

如果嵌套对象中呈现重名的映射抵触,能够手动指定起源哪个嵌套对象。

来看下生成的实现类源码:

@Componentpublic class UserNestedStructImpl implements UserNestedStruct {public UserNestedStructImpl() {}    public UserNestedDTO toUserNestedDTO(UserNestedDO userNestedDO) {if (userNestedDO == null) {return null;} else {UserNestedDTO userNestedDTO = new UserNestedDTO();            if (userNestedDO.getBirthday() != null) {userNestedDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userNestedDO.getBirthday()));            }            userNestedDTO.setMemo(this.userNestedDOUserExtDOMemo(userNestedDO));            userNestedDTO.setCity(this.userNestedDOUserAddressDOCity(userNestedDO));            userNestedDTO.setAddress(this.userNestedDOUserAddressDOAddress(userNestedDO));            userNestedDTO.setRegSource(this.userNestedDOUserExtDORegSource(userNestedDO));            userNestedDTO.setFavorite(this.userNestedDOUserExtDOFavorite(userNestedDO));            userNestedDTO.setSchool(this.userNestedDOUserExtDOSchool(userNestedDO));            userNestedDTO.setName(userNestedDO.getName());            userNestedDTO.setSex(userNestedDO.getSex());            userNestedDTO.setMarried(userNestedDO.isMarried());            userNestedDTO.setRegDate(DateFormatUtils.format(userNestedDO.getRegDate(), "yyyy-MM-dd HH:mm:ss"));            return userNestedDTO;        }    }    private String userNestedDOUserExtDOMemo(UserNestedDO userNestedDO) {if (userNestedDO == null) {return null;} else {UserExtDO userExtDO = userNestedDO.getUserExtDO();            if (userExtDO == null) {return null;} else {String memo = userExtDO.getMemo();                return memo == null ? null : memo;            }        }    }    private String userNestedDOUserAddressDOCity(UserNestedDO userNestedDO) {if (userNestedDO == null) {return null;} else {UserAddressDO userAddressDO = userNestedDO.getUserAddressDO();            if (userAddressDO == null) {return null;} else {String city = userAddressDO.getCity();                return city == null ? null : city;            }        }    }    private String userNestedDOUserAddressDOAddress(UserNestedDO userNestedDO) {if (userNestedDO == null) {return null;} else {UserAddressDO userAddressDO = userNestedDO.getUserAddressDO();            if (userAddressDO == null) {return null;} else {String address = userAddressDO.getAddress();                return address == null ? null : address;            }        }    }    private String userNestedDOUserExtDORegSource(UserNestedDO userNestedDO) {if (userNestedDO == null) {return null;} else {UserExtDO userExtDO = userNestedDO.getUserExtDO();            if (userExtDO == null) {return null;} else {String regSource = userExtDO.getRegSource();                return regSource == null ? null : regSource;            }        }    }    private String userNestedDOUserExtDOFavorite(UserNestedDO userNestedDO) {if (userNestedDO == null) {return null;} else {UserExtDO userExtDO = userNestedDO.getUserExtDO();            if (userExtDO == null) {return null;} else {String favorite = userExtDO.getFavorite();                return favorite == null ? null : favorite;            }        }    }    private String userNestedDOUserExtDOSchool(UserNestedDO userNestedDO) {if (userNestedDO == null) {return null;} else {UserExtDO userExtDO = userNestedDO.getUserExtDO();            if (userExtDO == null) {return null;} else {String school = userExtDO.getSchool();                return school == null ? null : school;            }        }    }}

从源码能够看到,从嵌套对象来的值都会新增一个办法判断一下,以避免出现空指定。

测试一下:

/** * 微信公众号:Java 技术栈 * @author 栈长 */@RunWith(SpringRunner.class)@SpringBootTestpublic class UserNestedStructTest {@Autowired    private UserNestedStruct userNestedStruct;    @Test    public void test1() {UserExtDO userExtDO = new UserExtDO();        userExtDO.setRegSource("公众号:Java 技术栈");        userExtDO.setFavorite("写代码");        userExtDO.setSchool("社会大学");        userExtDO.setKids(1);        userExtDO.setMemo("扩大信息");        UserAddressDO userAddressDO = new UserAddressDO();        userAddressDO.setProvince("广东省");        userAddressDO.setCity("深圳市");        userAddressDO.setPostcode("666666");        userAddressDO.setAddress("001 号大巷 Java 技术栈公众号");        userAddressDO.setMemo("地址信息");        UserNestedDO userNestedDO = new UserNestedDO();        userNestedDO.setName("栈长嵌套映射");        userNestedDO.setSex(1);        userNestedDO.setAge(18);        userNestedDO.setBirthday(new Date());        userNestedDO.setPhone("18888888888");        userNestedDO.setMarried(true);        userNestedDO.setRegDate(new Date());        userNestedDO.setMemo("666");        userNestedDO.setUserExtDO(userExtDO);        userNestedDO.setUserAddressDO(userAddressDO);        UserNestedDTO userNestedDTO = userNestedStruct.toUserNestedDTO(userNestedDO);        System.out.println("===== 嵌套映射 =====");        System.out.println(userNestedDTO);    }}

输入后果:

能够看到嵌套对象值,并且 memo 也是从指定的嵌套对象来的,后果验证胜利。

4、映射现有实例

以上介绍的都是映射并生成一个新的 DTO 实例,如果是已有的现有 DTO 实例呢,该怎么映射呢?

间接上代码:

/** * 微信公众号:Java 技术栈 * @author 栈长 */@Mapper(componentModel = "spring")public interface UserExistStruct {@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")    @Mapping(target = "regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(userDO.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")    @Mapping(source = "userExtDO.regSource", target = "registerSource")    @Mapping(source = "userExtDO.favorite", target = "favorite")    @Mapping(target = "name", ignore = true)    @Mapping(target = "memo", ignore = true)    void toUserShowDTO(@MappingTarget UserShowDTO userShowDTO, UserDO userDO);}

在办法上新增 DTO 对象参数并应用 @MappingTarget 对象润饰,参数地位能够调换。

来看下生成的实现类源码:

@Componentpublic class UserExistStructImpl implements UserExistStruct {public UserExistStructImpl() {}    public void toUserShowDTO(UserShowDTO userShowDTO, UserDO userDO) {if (userDO != null) {if (userDO.getBirthday() != null) {userShowDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userDO.getBirthday()));            } else {userShowDTO.setBirthday((String)null);            }            userShowDTO.setRegisterSource(this.userDOUserExtDORegSource(userDO));            userShowDTO.setFavorite(this.userDOUserExtDOFavorite(userDO));            userShowDTO.setSex(userDO.getSex());            userShowDTO.setMarried(userDO.isMarried());            userShowDTO.setRegDate(DateFormatUtils.format(userDO.getRegDate(), "yyyy-MM-dd HH:mm:ss"));        }    }    private String userDOUserExtDORegSource(UserDO userDO) {if (userDO == null) {return null;} else {UserExtDO userExtDO = userDO.getUserExtDO();            if (userExtDO == null) {return null;} else {String regSource = userExtDO.getRegSource();                return regSource == null ? null : regSource;            }        }    }    private String userDOUserExtDOFavorite(UserDO userDO) {if (userDO == null) {return null;} else {UserExtDO userExtDO = userDO.getUserExtDO();            if (userExtDO == null) {return null;} else {String favorite = userExtDO.getFavorite();                return favorite == null ? null : favorite;            }        }    }}

userShowDTO 是作为办法参数传入的,而不是新创建的。

测试一下:

/** * 微信公众号:Java 技术栈 * @author 栈长 */@RunWith(SpringRunner.class)@SpringBootTestpublic class UserExistStructTest {@Autowired    private UserExistStruct userExistStruct;    @Test    public void test1() {UserExtDO userExtDO = new UserExtDO();        userExtDO.setRegSource("公众号:Java 技术栈");        userExtDO.setFavorite("写代码");        userExtDO.setSchool("社会大学");        UserDO userDO = new UserDO();        userDO.setName("栈长");        userDO.setSex(1);        userDO.setAge(18);        userDO.setBirthday(new Date());        userDO.setPhone("18888888888");        userDO.setMarried(true);        userDO.setRegDate(new Date());        userDO.setMemo("666");        userDO.setUserExtDO(userExtDO);        System.out.println("===== 映射现有实例前 =====");        UserShowDTO userShowDTO = new UserShowDTO();        userShowDTO.setName("栈长 NAME");        userShowDTO.setMemo("栈长 MEMO");        System.out.println(userShowDTO);        System.out.println("===== 映射现有实例后 =====");        userExistStruct.toUserShowDTO(userShowDTO, userDO);        System.out.println(userShowDTO);    }}

输入后果:

能够看到已有 DTO 对象的值及新映射的值,后果验证胜利。

留神:默认是以笼罩原有值的形式映射的,如果要保留原有 XX 的值,应用 ignore 疏忽即可

总结

本文栈长介绍了 MapStruct 的 4 个高级玩法,足以应答各种 Bean 类映射了,其实还有很多简单的、个性化用法,一篇难以写完,栈长前面有工夫会整理出来,陆续给大家分享。

感兴趣的也能够参考官网文档:

https://mapstruct.org/documen…

另外,栈长始终介绍的是 DO –> DTO 的映射,其实反过来 DTO –> DO、BO 也是一样的,只是对象名称不一样,映射的用法是一样的,这样在服务 A 接管到服务 B 过去的 DTO 数据时,能够再进行一次反射映射供业务应用。

本文实战源代码完整版曾经上传:

https://github.com/javastacks…

欢送 Star 学习,前面 Spring Boot 示例都会在这下面提供!

好了,明天的分享就到这里了,前面栈长会分享更多好玩的 Java 技术和最新的技术资讯,关注公众号 Java 技术栈第一工夫推送,我也将支流 Java 面试题和参考答案都整顿好了,在公众号后盾回复关键字 “ 面试 ” 进行刷题。

最初,感觉我的文章对你用播种的话,动动小手,给个在看、转发,原创不易,栈长须要你的激励。

版权申明: 本文系公众号 “Java 技术栈 ” 原创,原创实属不易,转载、援用本文内容请注明出处,剽窃者一律举报+投诉,并保留追究其法律责任的权力。

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿 (2021 最新版)

2. 别在再满屏的 if/ else 了,试试策略模式,真香!!

3. 卧槽!Java 中的 xx ≠ null 是什么新语法?

4.Spring Boot 2.5 重磅公布,光明模式太炸了!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0