认识MapStruct

1、MapStruct是用来做什么的

在一个成熟的工程中,尤其是现在的分布式系统中,应用与应用之间,还有单独的应用细分模块之后,DO 一般不会让外部依赖,这时候需要在提供对外接口的模块里放 DTO 用于对象传输,也即是 DO 对象对内,DTO对象对外,DTO 可以根据业务需要变更,并不需要映射 DO 的全部属性。
这种对象与对象之间的互相转换,就需要有一个专门用来解决转换问题的工具,毕竟每一个字段都 get/set 会很麻烦。MapStruct 是一种属性映射工具,只需要定义一个 Mapper 接口,MapStruct 就会自动实现这个映射接口,避免了复杂繁琐的映射实现。

2、案例

创建实体 user.java

@Getter   
@Setter  
@NoArgsConstructor  
@AllArgsConstructor  
public class User {

private String userId;

private String name;

private Integer age;

private String email;

private Date birthDate;  
}

创建实体VO userVo.java

@Getter  
@Setter  
@AllArgsConstructor  
@NoArgsConstructor   
public class UserVo {

private String name;

private Integer age;

private String email;

private Date birth;

private String birthformat;  
}  

###创建Mapper UserConverterMapper.java

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

@Mapper(componentModel = "spring")  
public interface UserConverterMapper {  

UserConverterMapper INSTANCE = Mappers.getMapper(UserConverterMapper.class);

User UserVo2User(UserVo userVo);

@Mappings({
        //属性名不一致映射
        @Mapping(source = "birthdate", target = "birth"),
        //属性格式不一致
        @Mapping(target = "birthformat", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(person.getBirthdate(),\"yyyy-MM-dd HH:mm:ss\"))"),

})
UserVo User2UserVo(User user);

List<User> UsersVo2Users(List<UserVo> userVos);

List<UserVo> Users2UserVos(List<User> user);  
}

在Mapper接口定义方法上面声明了一系列的注解映射@Mapping以及@Mappings,那么这两个注解是用来干什么工作的呢?
@Mapping注解我们用到了两个属性,分别是source、target,参数名称和参数类型不一致的时候,可以使用映射参数关系。

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

target代表的是映射到方法方法值内的字段名称,配置如上面UserConverterMapper所示。
###Mapper的实现
MapStruct根据我们配置的@Mapping注解自动将source实体内的字段进行了调用target实体内字段的setXxx方法赋值,并且做出了一切参数验证。

@Component  
public class UserConverterMapperImpl implements UserConverterMapper {
public UserConverterMapperImpl() {
}

public User UserVo2User(UserVo userVo) {
    if (userVo == null) {
        return null;
    } else {
        User user = new User();
        user.setBirthDate(userVo.getBirth());
        user.setName(userVo.getName());
        user.setAge(userVo.getAge());
        user.setEmail(userVo.getEmail());
        return user;
    }
}

public UserVo User2UserVo(User user) {
    if (user == null) {
        return null;
    } else {
        UserVo userVo = new UserVo();
        userVo.setBirth(user.getBirthDate());
        userVo.setName(user.getName());
        userVo.setAge(user.getAge());
        userVo.setEmail(user.getEmail());
        userVo.setBirthformat(DateFormatUtils.format(user.getBirthDate(), "yyyy-MM-dd HH:mm:ss"));
        return userVo;
    }
}

public List<User> UsersVo2Users(List<UserVo> userVos) {
    if (userVos == null) {
        return null;
    } else {
        List<User> list = new ArrayList(userVos.size());
        Iterator var3 = userVos.iterator();

        while(var3.hasNext()) {
            UserVo userVo = (UserVo)var3.next();
            list.add(this.UserVo2User(userVo));
        }

        return list;
    }
}

public List<UserVo> Users2UserVos(List<User> user) {
    if (user == null) {
        return null;
    } else {
        List<UserVo> list = new ArrayList(user.size());
        Iterator var3 = user.iterator();

        while(var3.hasNext()) {
            User user1 = (User)var3.next();
            list.add(this.User2UserVo(user1));
        }

        return list;
    }
}
}

###测试和使用

测试验证:获取Mapper的方式就是采用Mappers通过动态工厂内部反射机制完成Mapper实现类的获取。

public class ConverterTest {

public static void main(String[] args) {
    UserVo userVo = new UserVo("张三",22,"1212@qq.com",new Date(),null);
    User user = UserConverterMapper.INSTANCE.UserVo2User(userVo);
    Assert.assertTrue(userVo.getName().equals(user.getName()));
    Assert.assertTrue(userVo.getAge().equals(user.getAge()));

    UserVo vo = UserConverterMapper.INSTANCE.User2UserVo(user);
    Assert.assertTrue(vo.getName().equals(user.getName()));
    Assert.assertTrue(vo.getAge().equals(user.getAge()));

}
}

使用:在@Mapper注解内添加componentModel属性值,配置后在外部可以采用@Autowired方式注入Mapper实现类完成映射方法调用,由mapstruct自动生成的类文件,会发现标记了@Component注解。

@Autowired
public UserConverterMapper userConverterMapper;

@RequestMapping(value = "/fetchUsers",method = RequestMethod.GET)
@ResponseBody
public BaseResult<List<UserVo>>  fetchBatch(String name){
    BaseResult<List<UserVo>>  result = new BaseResult();
    try{
        UserCondition condtion = new UserCondition();
        condtion.setName(name);
        List<User>  userList = userService.fetchUserList(condtion);
        List<UserVo> vos =this.userConverterMapper.Users2UserVos(userList);
        result.setCode(0);
        result.setMess("succ");
        result.setData(vos);
        return result;
    }catch (Exception e){
       return result;
    }

}

调用返回结果

{"code":0,  
"mess":"succ",  
"data":[
  {
   "name":"小米",
   "email":"1212@qq.com",
   "birth":"2019-08-14 03:34:56",
   "age":22
 }]
}

3、总结

  • 它可以自动封装一些同级,属性名相同的属性名如上name age 属性,可以无需手动写 @Mapping
  • 对于非同级或属性名 需要写相关的 @mapping 属性名不同可以查考 birth 属性配置。如果不同级@Mapping(source = “XXX.birthDate”, target = “XX.birth”) 如此就行了。
    @mapping 还有很多属性,可以查看API。
// ReticeWordDlg.h: 头文件 // #pragma once #include "CScreenWnd.h" #include <vector> typedef struct Word { char word[50]; char explain[50]; }Word; // CReticeWordDlg 对话框 class CReticeWordDlg : public CDialogEx { // 构造 public: CReticeWordDlg(CWnd* pParent = nullptr); // 标准构造函数 // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_RETICEWORD_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 void RandomGen();//随机抽取一个单词 void SaveUnknow();//将不认识的单词保存到文本文件 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); virtual BOOL OnInitDialog();//初始化函数 afx_msg void OnBnClickedButtonStart();//点击开始按钮 afx_msg void OnBnClickedButtonKnow();//点击认识按钮 afx_msg void OnBnClickedButtonUnknow();//点击不认识按钮 afx_msg void OnBnClickedButtonSkip();//点击跳过按钮 afx_msg void OnBnClickedButtonDetail();//点击显示注释按钮 DECLARE_MESSAGE_MAP() public: CScreenWnd m_screen;//自定义控件,用于显示单词以及释义 std::vector<Word> words;//总单词库 std::vector<Word> unknown;//不认识的单词库 BOOL mode;//FALSE表示从总单词库中随机抽取单词,TRUE表示从不认识的单词库中抽取单词 int randIndex;//当前抽取的单词在词库中的索引 CButton m_btnKnow;//认识按钮的控制变量,用于控制按钮是否可用 CButton m_btnSkip;//跳过按钮的控制变量,用于控制按钮是否可用 CButton m_btnStart;//开始按钮的控制变量,用于控制按钮是否可用 CButton m_btnUnknow;//不认识按钮的控制变量,用于控制按钮是否可用 };
06-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值