MapStruct 总结


title: MapStruct 总结
date: 2022-03-22 13:51:27
tags:

  • Java
    categories:
  • 开发技术及框架
    cover: https://cover.png
    feature: false

1. 概念

在系统工程开发过程中,会有各个层之间的对象转换,比如 VO、DTO、PO、DO 等,如果都是手动 setter、getter 特别浪费时间,还可能操作错误,所以选择一个自动化工具会更加方便。常见的有 json2Json、Apache 和 Spring 的 BeanUtils.copyProperties()、BeanCopier、JMapper 等

对象属性转换的操作无非是基于反射、AOP、CGlib、ASM、Javassist 在编译时和运行期进行处理,再有好的思路就是在编译前生成出对应的 setter、getter,就像手写出来的一样。MapStruct 就是直接在编译期生成对应的 setter、getter,性能更好、使用方便

2. POM

导入依赖

<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct</artifactId>
	<version>1.5.0.RC1</version>
</dependency>
<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct-processor</artifactId>
	<version>1.5.0.RC1</version>
</dependency>
<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct-jdk8</artifactId>
	<version>1.5.0.RC1</version>
</dependency>

可以选择在 IDEA 中下载其插件
在这里插入图片描述

3. 实体类

定义几个 DO 和 DTO

// UserDO
@Data
@Builder
public class UserDO {
    private Integer id;
    private String name;
    private Integer age;
    private String address;
    private String birthday;
    private String phone;
    private String userDOGender;
    private Date createTime;
    private Map<String, String> map;
    private List<PersonDO> personList;
}
// UserDTO
@Data
@Builder
public class UserDTO {
    private Integer id;
    private String name;
    private Integer age;
    private String address;
    private String userDTOGender;
    private Date createTime;
    private Map<String, String> map;
    private List<PersonDO> personList;
}
// PersonDO
@Data
@Builder
public class PersonDO {
    private Integer id;
    private String name;
    private String job;
    private String remark;
    private Date createTime;
}
// PersonDTO
@Data
@Builder
public class PersonDTO {
    private Integer id;
    private String name;
    private String job;
}

3. 映射器

  • 如果 DTO 和实体类中的字段名称是一致的,只需要写方法签名即可
  • 如果参数名称有变化,需要使用 @Maping 注解,source 为原参数名称,target 为转换后的类的参数名称
  • 隐式类型转换
    在许多情况下,MapStruct 会自动处理类型转换。如在 source 中是 int 类型但在 target 中是 String 类型,会自动进行转换。以及所有 Java 基本数据类型及其相应的包装类型,如 int 和 Integer,boolean 和 Boolean 等。还有所有 Java Number类型和包装器类型之间,如 int 和 long 或 byte 和 Integer
@Mapper // 直接使用
@Mapper(componentModel = "spring") // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可
public interface MapStruct {

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

    @Mapping(source = "userDOGender", target = "userDTOGender")
    UserDTO userDOToUserDTO(UserDO userDO);

    @Mapping(source = "userDTOGender", target = "userDOGender")
    UserDO UserDTOToUserDO(UserDTO userDTO);

    PersonDTO personDOToPersonDTO(PersonDO personDO);

    PersonDO PersonDTOToPersonDO(PersonDTO personDTO);
}

编译后,会在同级目录生成实现类:
在这里插入图片描述

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2022-03-22T12:16:06+0800",
    comments = "version: 1.5.0.RC1, compiler: javac, environment: Java 1.8.0_131 (Oracle Corporation)"
)
public class MapStructImpl implements MapStruct {

    @Override
    public UserDTO userDOToUserDTO(UserDO userDO) {
        if ( userDO == null ) {
            return null;
        }

        UserDTO.UserDTOBuilder userDTO = UserDTO.builder();

        userDTO.userDTOGender( userDO.getUserDOGender() );
        userDTO.id( userDO.getId() );
        userDTO.name( userDO.getName() );
        userDTO.age( userDO.getAge() );
        userDTO.address( userDO.getAddress() );
        userDTO.createTime( userDO.getCreateTime() );
        Map<String, String> map = userDO.getMap();
        if ( map != null ) {
            userDTO.map( new LinkedHashMap<String, String>( map ) );
        }
        List<PersonDO> list = userDO.getPersonList();
        if ( list != null ) {
            userDTO.personList( new ArrayList<PersonDO>( list ) );
        }

        return userDTO.build();
    }

    @Override
    public UserDO UserDTOToUserDO(UserDTO userDTO) {
        if ( userDTO == null ) {
            return null;
        }

        UserDO.UserDOBuilder userDO = UserDO.builder();

        userDO.userDOGender( userDTO.getUserDTOGender() );
        userDO.id( userDTO.getId() );
        userDO.name( userDTO.getName() );
        userDO.age( userDTO.getAge() );
        userDO.address( userDTO.getAddress() );
        userDO.createTime( userDTO.getCreateTime() );
        Map<String, String> map = userDTO.getMap();
        if ( map != null ) {
            userDO.map( new LinkedHashMap<String, String>( map ) );
        }
        List<PersonDO> list = userDTO.getPersonList();
        if ( list != null ) {
            userDO.personList( new ArrayList<PersonDO>( list ) );
        }

        return userDO.build();
    }

    @Override
    public PersonDTO personDOToPersonDTO(PersonDO personDO) {
        if ( personDO == null ) {
            return null;
        }

        PersonDTO.PersonDTOBuilder personDTO = PersonDTO.builder();

        personDTO.id( personDO.getId() );
        personDTO.name( personDO.getName() );
        personDTO.job( personDO.getJob() );

        return personDTO.build();
    }

    @Override
    public PersonDO PersonDTOToPersonDO(PersonDTO personDTO) {
        if ( personDTO == null ) {
            return null;
        }

        PersonDO.PersonDOBuilder personDO = PersonDO.builder();

        personDO.id( personDTO.getId() );
        personDO.name( personDTO.getName() );
        personDO.job( personDTO.getJob() );

        return personDO.build();
    }
}

4. 测试

public class Test {
    public static void main(String[] args) {
        UserDO userDO = UserDO.builder().id(1)
                .name("张三")
                .age(18)
                .birthday("2003-01-04")
                .phone("12343")
                .userDOGender("男")
                .createTime(new Date(System.currentTimeMillis()))
                .map(new HashMap<String, String>(){
                    {
                        this.put("key", "value");
                    }
                })
                .personList(new ArrayList<PersonDO>(){
                    {
                        this.add(PersonDO.builder().id(11).build());
                    }
                }).build();
        UserDTO userDTO = Mappers.getMapper(MapStruct.class).userDOToUserDTO(userDO);
        System.out.println(userDTO);

        PersonDTO personDTO = PersonDTO.builder().id(11)
                .name("李四")
                .job("开发").build();
        PersonDO personDO = MapStruct.INSTANCE.PersonDTOToPersonDO(personDTO);
        System.out.println(personDO);
    }
}

在这里插入图片描述

5. 自定义转换规则

MapStruct 只提供了隐式类型转换和默认的一些转换,假如需要特定的类型转换,如将 java.sql.Timestamp 日期转换为只保留年月日的字符串等,可以自定义转换规则

5.1 使用 expression 表达式

1、创建一个转换规则类

public class MapStructRule {
    public static String toDate(Timestamp date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        return simpleDateFormat.format(date);
    }
}

2、在映射器对应的转换方法上使用 @Mapping 注解的 expression 属性标识转换规则,这里的属性值为需要写 全类名.方法名(参数),使用了 expression 就不需要 source 属性

@Mapper(componentModel = "spring") // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可
public interface MapStruct {
    MapStruct INSTANCE = Mappers.getMapper(MapStruct.class);

    @Mapping(target = "createTime", expression = "java(fan.fanblog.utils.MapStructRule.toDate(menuDO.getCreateTime()))")
    @Mapping(target = "updateTime", expression = "java(fan.fanblog.utils.MapStructRule.toDate(menuDO.getUpdateTime()))")
    MenuVO MenuDOToMenuVO(MenuDO menuDO);
}

3、测试类

public class Demo {
    public static void main(String[] args) {
        MenuDO menuDO = new MenuDO();

        menuDO.setCreateTime(new Timestamp(System.currentTimeMillis()));
        menuDO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
        System.out.println(menuDO);

        MenuVO menuVO = MapStruct.INSTANCE.MenuDOToMenuVO(menuDO);
        System.out.println(menuVO);
    }
}

在这里插入图片描述

5.2 使用 @Named 注解

1、创建一个转换规则类,在对应的转换方法上标注 @Named 注解表示转换方法名

public class MapStructRule {
    @Named("toDate")
    public static String toDate(Timestamp date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return simpleDateFormat.format(date);
    }
}

2、在映射器上使用 @Mapperuses 属性,属性值为转换规则类的 Class 对象,然后再映射器对应的方法上使用 @Mapping 注解的 qualifiedByName 属性,属性值为转换规则类 @Named 定义的方法名

@Mapper(componentModel = "spring", uses = MapStructRule.class) // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可
public interface MapStruct {
    MapStruct INSTANCE = Mappers.getMapper(MapStruct.class);

    @Mapping(target = "createTime", source = "createTime", qualifiedByName = "toDate")
    @Mapping(target = "updateTime", source = "updateTime", qualifiedByName = "toDate")
    MenuVO MenuDOToMenuVO(MenuDO menuDO);
}

3、测试类

public class Demo {
    public static void main(String[] args) {
        MenuDO menuDO = new MenuDO();

        menuDO.setCreateTime(new Timestamp(System.currentTimeMillis()));
        menuDO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
        System.out.println(menuDO);

        MenuVO menuVO = MapStruct.INSTANCE.MenuDOToMenuVO(menuDO);
        System.out.println(menuVO);
    }
}

在这里插入图片描述

6. 忽略属性不转换

@Mapper(componentModel = "spring", uses = MapStructRule.class) // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可
public interface MapStruct {
    MapStruct INSTANCE = Mappers.getMapper(MapStruct.class);

    @Mapping(target = "createTime", ignore = true)
    @Mapping(target = "updateTime", ignore = true)
    MenuDO MenuVOToMenuDO(MenuVO menuVO);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
刚刚学习Python, 边看廖神的教程边想着搞个离线版,用requests+beautifulsoup抓的。仅供学习用 Python教程 Python简介 安装Python Python解释器 第一个Python程序 使用文本编辑器 Python代码运行助手 输入和输出 Python基础 数据类型和变量 字符串和编码 使用list和tuple 条件判断 循环 使用dict和set 函数 调用函数 定义函数 函数的参数 递归函数 高级特性 切片 迭代 列表生成式 生成器 迭代器 函数式编程 高阶函数 map/reduce filter sorted 返回函数 匿名函数 装饰器 偏函数 模块 使用模块 安装第三方模块 单元测试 文档测试 IO编程 文件读写 StringIO和BytesIO 操作文件和目录 序列化 进程和线程 多进程 多线程 ThreadLocal 进程 vs. 线程 分布式进程 正则表达式 常用内建模块 datetime collections base64 struct hashlib hmac itertools contextlib urllib XML HTMLParser 常用第三方模块 Pillow requests chardet psutil virtualenv 图形界面 网络编程 TCP/IP简介 TCP编程 UDP编程 电子邮件 SMTP发送邮件 POP3收取邮件 访问数据库 使用SQLite 使用MySQL 使用SQLAlchemy Web开发 HTTP协议简介 HTML简介 WSGI接口 使用Web框架 使用模板 异步IO 协程 asyncio async/await aiohttp 实战 file:///E|/Personal/PythonCreep/liaoxuefeng/0_index.html[2018-6-7 下午 08:03:16] Day 1 - 搭建开发环境 Day 2 - 编写Web App骨架 Day 3 - 编写ORM Day 4 - 编写Model Day 5 - 编写Web框架 Day 6 - 编写配置文件 Day 7 - 编写MVC Day 8 - 构建前端 Day 9 - 编写API Day 10 - 用户注册和登录 Day 11 - 编写日志创建页 Day 12 - 编写日志列表页 Day 13 - 提升开发效率 Day 14 - 完成Web App Day 15 - 部署Web App Day 16 - 编写移动App FAQ 期末总结
在使用MapStruct时,可能会遇到一些冲突。其中一个常见的冲突是与Lombok的@Builder注解相关的问题。当使用@Builder注解时,MapStruct在编译时会使用BeanBuilder.build()方法来完成目标对象的实例化。这可能导致与MapStruct生成的代码冲突。 为了解决这个冲突,可以将@MappingTarget修饰的目标对象改为使用由@Builder注解引入的Bean.Builder的构造器。这样可以避免与Lombok的@Builder注解产生冲突,并正确地使用MapStruct总结起来,当在使用MapStruct时遇到冲突时,可以采取以下步骤来解决问题: 1. 确定冲突的具体原因是什么,例如是否与Lombok的@Builder注解有关。 2. 根据冲突的特点和需求,选择适当的解决方案。 3. 根据解决方案,对相关的注解或代码进行修改,以解决冲突。 4. 确保修改后的代码能够正确地使用MapStruct,并且不再产生冲突。 需要注意的是,在解决冲突时,要仔细阅读相关的文档和参考资料,以确保理解和正确实施解决方案,以避免出现其他问题。 希望这些信息能够帮助您解决MapStruct的冲突问题。如果还有其他问题,请随时提问。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [[Solutions] <MapStruct> 与@Builder冲突, @AfterMapping不生效](https://blog.csdn.net/nxhljt/article/details/123893492)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fan 

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值