Java对象字段拷贝最佳实践分享

 更新时间:2025年03月03日 08:29:15   作者:码农研究僧  
文章介绍了几种常见的对象字段拷贝方法,包括手动set、BeanUtils.copyProperties、Lombok的@Builder和MapStruct,每种方法都有其优缺点和适用场景,推荐使用MapStruct,因为它在编译期生成代码,性能最优,支持复杂对象映射,需要的朋友可以参考下

前言

一开始在线表和历史表都是一张表,只不过字段设置不一样,显示不一样

但后续数据越来越多,为了不影响在线表,数据最终得落入历史表,不影响在线表的CRUD

以下章节围绕如何克隆在线表的数据

对象字段拷贝 的需求,比如从数据库查询出的对象需要转换成 DTO,或者在审核流程中更新一张表的同时写入历史表等

如果手动 set 字段,代码会变得繁琐,难以维护

1. 传统set(不推荐)

最简单的方法是 手动赋值,但是当字段较多时,代码冗长且容易遗漏。

示例代码:

CheckBoxDetailDO checkBoxDetailDO = new CheckBoxDetailDO();
checkBoxDetailDO.setCheckStatus(1L);
checkBoxDetailDO.setCntr(checkBox.getCntr());
checkBoxDetailDO.setImgCntrF(checkBox.getImgCntrF());
checkBoxDetailDO.setCreateTime(checkBox.getCreateTime());

缺点:

  • 代码冗长:如果 CheckBoxDO有几十个字段,手写 set 非常麻烦
  • 易出错:如果 CheckBoxDO结构变化,必须手动修改所有 set 逻辑,维护成本高

适用场景:

字段较少(少于 3 个字段)

除了set,还有一种跟他很像,我也放在这个章节

Lombok 的 @Builder,可以使用 builder() 方法来 链式赋值,提高可读性(但我感觉没啥差异)

CheckBoxDetailDO checkBoxDetailDO = CheckBoxDetailDO.builder()
        .checkStatus(1L)
        .cntr(checkBox.getCntr())
        .imgCntrF(checkBox.getImgCntrF())
        .createTime(checkBox.getCreateTime())
        .build();

2. copyProperties(有局限)

CheckBoxDetailDO checkBoxDetailDO = new CheckBoxDetailDO();
BeanUtils.copyProperties(checkBox, checkBoxDetailDO);
checkBoxDetailDO.setCheckStatus(1L); // 额外赋值

优点:

  • 代码简洁,自动拷贝 相同字段,避免手动 set
  • 无需额外依赖,Spring 内置

缺点:

  • 性能一般,使用了 反射,比手动 set 慢
  • 字段名必须完全匹配,如果 CheckBoxDetailDO 和 CheckBoxDO字段名不一样,无法拷贝
  • 不支持复杂转换,比如 数据类型不同(int vs String)、默认值设置 等

适用场景:

  • 字段名和类型完全匹配的简单拷贝
  • 项目已经使用 Spring,避免额外依赖

3. MapStruct(推荐)

如果 CheckBoxDO和 CheckBoxDetailDO 结构类似,并且字段较多,推荐使用 MapStruct 进行自动对象映射
MapStruct 在编译期生成代码,相比 BeanUtils 性能更优,并且支持字段转换

1、定义转换接口

创建一个 Mapper 接口,并用 @Mapper 注解标识。

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper(componentModel = "spring")
public interface CheckBoxConverter {
    CheckBoxConverter INSTANCE = Mappers.getMapper(CheckBoxConverter.class);

    @Mapping(target = "checkStatus", constant = "1L") // 强制设定 checkStatus 为 1
    CheckBoxDetailDO toDetailDO(CheckBoxDO checkBox);
}

2、调用转换

@Resource
private ChekBoxDetailMapper chekBoxDetailMapper;

CheckBoxDetailDO checkBoxDetailDO = CheckBoxConverter.INSTANCE.toDetailDO(checkBox);
chekBoxDetailMapper.insert(checkBoxDetailDO);

优点:

  • 高性能,编译期生成代码,没有反射开销
  • 自动映射字段,省去 set 代码
  • 支持类型转换,例如 String -> Long、Date -> LocalDateTime 等
  • 字段名不同也能映射,可以用 @Mapping(source = “oldField”, target = “newField”) 自定义映射关系

缺点:

需要引入 MapStruct 依赖,但一次配置,终身受益

适用场景:

  • 字段较多且映射规则较复杂
  • 项目对性能要求较高(比 BeanUtils 更快)

但是会有bug:

后续发现id自增字段也被复刻了!

采取忽略的方式:

@Mapper
public interface CheckBoxConverter {
    CheckBoxConverter INSTANCE = Mappers.getMapper(CheckBoxConverter.class);

    @Mapping(target = "id", ignore = true) // 忽略 id 字段
    @Mapping(target = "checkStatus", constant = "1L") // 强制设定 checkStatus 为 1
    CheckBoxDetailDO toDetailDO(CheckBoxDO checkBox);
}

截图如下:

这里拓展下这种方式其他的知识点:

使用 @BeanMapping(ignoreByDefault = true)(仅拷贝指定字段)

类似如下代码:

@Mapper(componentModel = "spring")
public interface CheckBoxConverter {
    CheckBoxConverter INSTANCE = Mappers.getMapper(CheckBoxConverter.class);

    @BeanMapping(ignoreByDefault = true)
    @Mapping(target = "cntr", source = "cntr")
    @Mapping(target = "imgCntrF", source = "imgCntrF")
    @Mapping(target = "createTime", source = "createTime")
    @Mapping(target = "checkStatus", constant = "1L")
    CheckBoxDetailDO toDetailDO(CheckBox checkBox);
}

如果不想修改代码:

CheckBoxDetailDO checkBoxDetailDO = CheckBoxConverter.INSTANCE.toDetailDO(checkBox);
checkBoxDetailDO.setId(null); // 手动清除 id

最后,不要忘记insert,否则它只是一个对象,没有存储

CheckBoxDetailDO checkBoxDetailDO = CheckBoxConverter.INSTANCE.toDetailDO(checkBox);
checkBoxDetailMapper.insert(checkBoxDetailDO); // 插入数据库

4. 总结

方案代码简洁度性能适用场景
手动 set❌ 差
BeanUtils.copyProperties✅ 好❌一般字段完全匹配,简单拷贝
Lombok @Builder✅ 好✅ 快代码可读性强,构建新对象
MapStruct✅ 最优✅ 最优复杂对象映射,性能高

以上就是Java对象字段拷贝最佳实践分享的详细内容,更多关于Java对象字段拷贝的资料请关注脚本之家其它相关文章!

相关文章

  • struts2实现多文件上传

    struts2实现多文件上传

    这篇文章主要为大家详细介绍了struts2实现多文件上传,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-08-08
  • spring的applicationContext.xml文件与NamespaceHandler解析

    spring的applicationContext.xml文件与NamespaceHandler解析

    这篇文章主要介绍了spring的applicationContext.xml文件与NamespaceHandler解析,Spring容器启动,在创建BeanFactory时,需要加载和解析当前ApplicationContext对应的配置文件applicationContext.xml,从而获取bean相关的配置信息,需要的朋友可以参考下
    2023-12-12
  • java从字符串中提取数字的简单实例

    java从字符串中提取数字的简单实例

    下面小编就为大家带来一篇java从字符串中提取数字的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-10-10
  • 详解Java线程-守护线程与用户线程

    详解Java线程-守护线程与用户线程

    这篇文章主要介绍了Java守护线程与用户线程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • SpringBoot日志框架之Log4j2快速入门与参数详解

    SpringBoot日志框架之Log4j2快速入门与参数详解

    本文介绍了SpringBoot日志框架log4j2的基本使用和配置方法,包括将日志输出到控制台、文件、Elasticsearch和Kafka,多个输出目的地的配置,异步日志记录器的使用以及log4j2.xml配置文件的详细语法和参数含义,需要的朋友可以参考下
    2023-05-05
  • springboot aop里的@Pointcut()的配置方式

    springboot aop里的@Pointcut()的配置方式

    这篇文章主要介绍了springboot aop里的@Pointcut()的配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • 深入了解Java File分隔符和Path分隔符的使用

    深入了解Java File分隔符和Path分隔符的使用

    不同的操作系统使用不同的字符作为文件和路径分隔符。当我们的应用程序需要在多个平台上运行时,我们需要正确处理这些问题。Java帮助我们选择一个合适的分隔符,本文就来聊聊Java中File分隔符和 Path分隔符的使用
    2022-07-07
  • java使用nio2拷贝文件的示例

    java使用nio2拷贝文件的示例

    这篇文章主要介绍了java使用nio2拷贝文件的示例,需要的朋友可以参考下
    2014-04-04
  • 使用Java实现Excel转PDF的示例详解

    使用Java实现Excel转PDF的示例详解

    在实际的开发过程中,我们常常会遇到需要将 Excel 文件转换为 PDF 文件的需求,本文为大家介绍一种Java中的常见实现方式,需要的可以参考一下
    2025-02-02
  • 解决MultipartFile.transferTo(dest) 报FileNotFoundExcep的问题

    解决MultipartFile.transferTo(dest) 报FileNotFoundExcep的问题

    这篇文章主要介绍了解决MultipartFile.transferTo(dest) 报FileNotFoundExcep的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07

最新评论