Java對(duì)象字段拷貝最佳實(shí)踐分享
前言
一開(kāi)始在線表和歷史表都是一張表,只不過(guò)字段設(shè)置不一樣,顯示不一樣
但后續(xù)數(shù)據(jù)越來(lái)越多,為了不影響在線表,數(shù)據(jù)最終得落入歷史表,不影響在線表的CRUD
以下章節(jié)圍繞如何克隆在線表的數(shù)據(jù)
對(duì)象字段拷貝 的需求,比如從數(shù)據(jù)庫(kù)查詢出的對(duì)象需要轉(zhuǎn)換成 DTO,或者在審核流程中更新一張表的同時(shí)寫(xiě)入歷史表等
如果手動(dòng) set 字段,代碼會(huì)變得繁瑣,難以維護(hù)
1. 傳統(tǒng)set(不推薦)
最簡(jiǎn)單的方法是 手動(dòng)賦值,但是當(dāng)字段較多時(shí),代碼冗長(zhǎng)且容易遺漏。
示例代碼:
CheckBoxDetailDO checkBoxDetailDO = new CheckBoxDetailDO(); checkBoxDetailDO.setCheckStatus(1L); checkBoxDetailDO.setCntr(checkBox.getCntr()); checkBoxDetailDO.setImgCntrF(checkBox.getImgCntrF()); checkBoxDetailDO.setCreateTime(checkBox.getCreateTime());
缺點(diǎn):
- 代碼冗長(zhǎng):如果 CheckBoxDO有幾十個(gè)字段,手寫(xiě) set 非常麻煩
- 易出錯(cuò):如果 CheckBoxDO結(jié)構(gòu)變化,必須手動(dòng)修改所有 set 邏輯,維護(hù)成本高
適用場(chǎng)景:
字段較少(少于 3 個(gè)字段)
除了set,還有一種跟他很像,我也放在這個(gè)章節(jié)
Lombok 的 @Builder,可以使用 builder() 方法來(lái) 鏈?zhǔn)劫x值,提高可讀性(但我感覺(jué)沒(méi)啥差異)
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); // 額外賦值
優(yōu)點(diǎn):
- 代碼簡(jiǎn)潔,自動(dòng)拷貝 相同字段,避免手動(dòng) set
- 無(wú)需額外依賴,Spring 內(nèi)置
缺點(diǎn):
- 性能一般,使用了 反射,比手動(dòng) set 慢
- 字段名必須完全匹配,如果 CheckBoxDetailDO 和 CheckBoxDO字段名不一樣,無(wú)法拷貝
- 不支持復(fù)雜轉(zhuǎn)換,比如 數(shù)據(jù)類(lèi)型不同(int vs String)、默認(rèn)值設(shè)置 等
適用場(chǎng)景:
- 字段名和類(lèi)型完全匹配的簡(jiǎn)單拷貝
- 項(xiàng)目已經(jīng)使用 Spring,避免額外依賴
3. MapStruct(推薦)
如果 CheckBoxDO和 CheckBoxDetailDO 結(jié)構(gòu)類(lèi)似,并且字段較多,推薦使用 MapStruct 進(jìn)行自動(dòng)對(duì)象映射
MapStruct 在編譯期生成代碼,相比 BeanUtils 性能更優(yōu),并且支持字段轉(zhuǎn)換
1、定義轉(zhuǎn)換接口
創(chuàng)建一個(gè) Mapper 接口,并用 @Mapper 注解標(biāo)識(shí)。
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") // 強(qiáng)制設(shè)定 checkStatus 為 1
CheckBoxDetailDO toDetailDO(CheckBoxDO checkBox);
}
2、調(diào)用轉(zhuǎn)換
@Resource private ChekBoxDetailMapper chekBoxDetailMapper; CheckBoxDetailDO checkBoxDetailDO = CheckBoxConverter.INSTANCE.toDetailDO(checkBox); chekBoxDetailMapper.insert(checkBoxDetailDO);
優(yōu)點(diǎn):
- 高性能,編譯期生成代碼,沒(méi)有反射開(kāi)銷(xiāo)
- 自動(dòng)映射字段,省去 set 代碼
- 支持類(lèi)型轉(zhuǎn)換,例如 String -> Long、Date -> LocalDateTime 等
- 字段名不同也能映射,可以用 @Mapping(source = “oldField”, target = “newField”) 自定義映射關(guān)系
缺點(diǎn):
需要引入 MapStruct 依賴,但一次配置,終身受益
適用場(chǎng)景:
- 字段較多且映射規(guī)則較復(fù)雜
- 項(xiàng)目對(duì)性能要求較高(比 BeanUtils 更快)
但是會(huì)有bug:

后續(xù)發(fā)現(xiàn)id自增字段也被復(fù)刻了!
采取忽略的方式:
@Mapper
public interface CheckBoxConverter {
CheckBoxConverter INSTANCE = Mappers.getMapper(CheckBoxConverter.class);
@Mapping(target = "id", ignore = true) // 忽略 id 字段
@Mapping(target = "checkStatus", constant = "1L") // 強(qiáng)制設(shè)定 checkStatus 為 1
CheckBoxDetailDO toDetailDO(CheckBoxDO checkBox);
}
截圖如下:

這里拓展下這種方式其他的知識(shí)點(diǎn):
使用 @BeanMapping(ignoreByDefault = true)(僅拷貝指定字段)
類(lèi)似如下代碼:
@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); // 手動(dòng)清除 id
最后,不要忘記insert,否則它只是一個(gè)對(duì)象,沒(méi)有存儲(chǔ)
CheckBoxDetailDO checkBoxDetailDO = CheckBoxConverter.INSTANCE.toDetailDO(checkBox); checkBoxDetailMapper.insert(checkBoxDetailDO); // 插入數(shù)據(jù)庫(kù)
4. 總結(jié)
| 方案 | 代碼簡(jiǎn)潔度 | 性能 | 適用場(chǎng)景 |
|---|---|---|---|
| 手動(dòng) set | ? 差 | ? | 快 |
| BeanUtils.copyProperties | ? 好 | ?一般 | 字段完全匹配,簡(jiǎn)單拷貝 |
| Lombok @Builder | ? 好 | ? 快 | 代碼可讀性強(qiáng),構(gòu)建新對(duì)象 |
| MapStruct | ? 最優(yōu) | ? 最優(yōu) | 復(fù)雜對(duì)象映射,性能高 |
以上就是Java對(duì)象字段拷貝最佳實(shí)踐分享的詳細(xì)內(nèi)容,更多關(guān)于Java對(duì)象字段拷貝的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring?Boot網(wǎng)絡(luò)配置之server.address超詳細(xì)講解
在Spring Boot中,我們可以通過(guò)配置server.address屬性來(lái)指定應(yīng)用程序綁定的IP地址,下面這篇文章主要介紹了Spring?Boot網(wǎng)絡(luò)配置之server.address的相關(guān)資料,需要的朋友可以參考下2025-09-09
Java中的CopyOnWriteArrayList原理詳解
這篇文章主要介紹了Java中的CopyOnWriteArrayList原理詳解,如源碼所示,CopyOnWriteArrayList和ArrayList一樣,都在內(nèi)部維護(hù)了一個(gè)數(shù)組,操作CopyOnWriteArrayList其實(shí)就是在操作內(nèi)部的數(shù)組,需要的朋友可以參考下2023-12-12
Springboot多數(shù)據(jù)源配置之整合dynamic-datasource方式
這篇文章主要介紹了Springboot多數(shù)據(jù)源配置之整合dynamic-datasource方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
Java接口回調(diào)和方法回調(diào)的簡(jiǎn)單實(shí)現(xiàn)步驟
這篇文章主要介紹了Java接口回調(diào)和方法回調(diào)的相關(guān)資料,接口回調(diào)是一種設(shè)計(jì)模式,實(shí)現(xiàn)三方解耦,調(diào)用者提供接口實(shí)現(xiàn),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-03-03
利用POI讀取word、Excel文件的最佳實(shí)踐教程
Apache POI 是用Java編寫(xiě)的免費(fèi)開(kāi)源的跨平臺(tái)的 Java API,Apache POI提供API給Java程式對(duì)Microsoft Office格式檔案讀和寫(xiě)的功能。 下面這篇文章主要給大家介紹了關(guān)于利用POI讀取word、Excel文件的最佳實(shí)踐的相關(guān)資料,需要的朋友可以參考下。2017-11-11
IDEA使用GsonFormat完成JSON和JavaBean之間的轉(zhuǎn)換
這篇文章主要介紹了IDEA使用GsonFormat完成JSON和JavaBean之間的轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03

