
本文深入探讨了mapstruct在更新现有目标对象时可能遇到的常见问题,主要包括由于字段不可变性(`final`修饰符)导致的更新失败,以及ide或构建工具未正确重新编译和生成mapstruct映射器代码引发的隐藏问题。通过具体代码示例和解决方案,文章强调了目标对象可变性(需要setter方法)对于更新操作的重要性,并提供了确保mapstruct正常工作的构建环境配置建议。
MapStruct是一个强大的J*a Bean映射工具,它通过编译时生成代码来简化对象之间的转换。除了创建新的目标对象外,MapStruct还支持更新现有目标对象的功能,这对于避免不必要的对象创建和提高性能非常有用。然而,在使用此功能时,开发者可能会遇到一些意想不到的问题。
MapStruct通过@MappingTarget注解来标识需要更新的目标对象。当一个映射方法包含@MappingTarget参数时,MapStruct会尝试将源对象的属性值复制到这个现有目标对象的相应属性中。
考虑以下示例,我们定义了Source和Destination两个类,以及一个用于映射的Mapper接口:
// Source.j*a
public class Source {
private String id;
private String other;
// 构造函数和getter
public Source(String id, String other) {
this.id = id;
this.other = other;
}
public String getId() { return id; }
public String getOther() { return other; }
// 如果需要更新Source,也需要setter
public void setId(String id) { this.id = id; }
public void setOther(String other) { this.other = other; }
}
// Destination.j*a
public class Destination {
private String id;
private String other;
// 构造函数和getter
public Destination(String id, String other) {
this.id = id;
this.other = other;
}
public String getId() { return id; }
public String getOther() { return other; }
// 注意:此处需要setter方法才能被MapStruct更新
public void setId(String id) { this.id = id; }
public void setOther(String other) { this.other = other; }
}
// Mapper.j*a
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
@Mapper
public interface MyMapper { // 更改接口名为MyMapper以避免与j*a.lang.Mapper冲突
MyMapper INSTANCE = Mappers.getMapper(MyMapper.class );
Destination createDestinationFromSource(Source source);
void updateDestinationFromSource(Source source, @MappingTarget Destination destination);
}在上述MyMapper接口中,createDestinationFromSource方法用于创建新的Destination实例,而updateDestinationFromSource方法则用于更新一个已存在的Destination实例。
开发者在使用updateDestinationFromSource方法进行测试时,可能会遇到更新不生效的问题,即使看起来代码逻辑是正确的。这通常由以下两个主要原因造成:
现象: 在IDE(如IntelliJ IDEA)中运行测试时,即使修改了代码,更新操作依然失败。然而,通过命令行执行构建命令后,问题却解决了。
原因分析: MapStruct是一个注解处理器,它在编译时生成映射器的实现代码。如果IDE的自动编译或增量编译机制未能正确触发MapStruct处理器的重新运行,或者未将生成的代码包含在测试运行路径中,那么在测试时使用的仍然是旧的或不完整的映射器实现。这会导致updateDestinationFromSource方法实际上没有执行任何更新逻辑。
解决方案: 确保每次修改MapStruct相关的接口或Bean定义后,都进行一次彻底的编译。对于M*en项目,最可靠的方法是在项目根目录执行:
mvn clean compile
clean命令会删除旧的编译产物,compile命令则会重新编译所有源文件并运行注解处理器,确保MapStruct生成最新的映射器实现。在IDE中,也可以尝试手动触发“Rebuild Project”或“Invalidate Caches / Restart”来解决。
独响
一个轻笔记+角色扮演的app
249
查看详情
现象: 即使解决了编译问题,更新操作仍然可能失败,或者仅在目标对象字段不再是final且添加了setter方法后才成功。
原因分析: MapStruct在更新现有对象时,需要能够修改目标对象的属性。如果目标对象的属性被final关键字修饰,那么这些属性在对象构造后就不能再被修改,MapStruct自然无法对其进行更新。同样,如果目标对象缺少相应的setter方法,MapStruct也无法通过标准J*a Bean约定来设置属性值。
解决方案: 对于需要通过MapStruct更新的目标对象(如Destination),其可被映射的属性不能被final修饰,并且必须提供公共的setter方法。
让我们修改Destination类以支持更新:
// Corrected Destination.j*a for update operations
public class Destination {
private String id;
private String other;
public Destination(String id, String other) {
this.id = id;
this.other = other;
}
public String getId() { return id; }
public String getOther() { return other; }
// 关键:添加setter方法以允许MapStruct更新
public void setId(String id) { this.id = id; }
public void setOther(String other) { this.other = other; }
}对比创建与更新: 值得注意的是,MapStruct在创建新的目标对象时,即使目标对象的字段是final的,只要提供了匹配的构造函数,MapStruct依然可以成功地通过构造函数初始化这些final字段。但对于更新操作,由于对象已经存在,无法再次调用构造函数,因此必须依赖setter方法来修改属性。
以下是结合上述解决方案的完整测试代码:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class MapStructUpdateTest {
@Test
void testMapStructUpdate() {
// 创建操作:验证初始映射
var source1 = new Source("sourceId1", "sourceOther1");
var destination1 = MyMapper.INSTANCE.createDestinationFromSource(source1);
Assertions.assertEquals(source1.getId(), destination1.getId());
Assertions.assertEquals(source1.getOther(), destination1.getOther());
System.out.println("Created Destination: " + destination1.getId() + ", " + destination1.getOther());
// 更新操作:准备一个已存在的Destination对象
var destinationToUpdate = new Destination("initialDestId", "initialDestOther");
System.out.println("Before Update: " + destinationToUpdate.getId() + ", " + destinationToUpdate.getOther());
// 准备用于更新的Source对象
var sourceForUpdate = new Source("newSourceId", "newSourceOther");
// 执行更新
MyMapper.INSTANCE.updateDestinationFromSource(sourceForUpdate, destinationToUpdate);
// 验证更新是否成功
Assertions.assertEquals(sourceForUpdate.getId(), destinationToUpdate.getId());
Assertions.assertEquals(sourceForUpdate.getOther(), destinationToUpdate.getOther());
System.out.println("After Update: " + destinationToUpdate.getId() + ", " + destinationToUpdate.getOther());
}
}运行此测试前,请务必执行mvn clean compile确保MapStruct生成了正确的映射器代码,并且Destination类已如上所示添加了setter方法。
理解这些核心原则和常见陷阱,将有助于开发者更高效、准确地使用MapStruct进行对象映射和更新。
以上就是MapStruct更新现有目标对象:解决字段不可变与编译问题的详细内容,更多请关注其它相关文章!
相关文章:
俄罗斯方块最新版入口 俄罗斯方块在线玩官网入口
如何在 Windows 11 中启动游戏手柄设置
小猿搜题在线学习页面在哪_小猿搜题在线学习中心入口
Safari怎么安装扩展程序 浏览器插件安装与管理方法【详解】
三星ZFold5多任务卡顿_Samsung ZFold5流畅度提升
动漫岛观看全网网 动漫岛在线正版动漫入口
如何优雅地解决Livewire文件上传难题?SpatieLivewireFilepond让一切变得简单
2025-2030年全球乘用车销量预测:新能源成增长主力
J*aScript map 方法中处理循环元素为空数组的策略
Python Socket多播通信中指定源IP地址的实践指南
Lar*el 8 多关键词数据库搜索优化实践
必由学官方登录入口 必由学教师学生账号快速访问
c++中的std::forward_list和std::list有什么不同_c++ forward_list与list区别分析
PHP字符串中复杂变量插值的最佳实践与语法解析
win11跳过OOBE三种方法 Win11跳过OOBE设置步骤
2026春节假期票务安排_2026春节放假购票指南
支付宝如何管理隐私设置_支付宝隐私保护的配置技巧
《主播少女的秘密账号迷宫》首支宣传片
AO3访问入口汇总 AO3网页版同人作品一键直达
Go语言中动态执行代码字符串的策略与实践
Python实时数据流中的动态最值查找策略
Lar*el表单中优雅地处理“返回”按钮以规避验证:最佳实践指南
怎么搭建一个php网站源码_搭php网站源码搭建教程
Composer中的^和~符号代表什么_精通Composer版本号语义化约束
C++的std::mdspan是什么_C++23中用于操作多维数组的非拥有视图
Python中如何避免重复条件判断:利用数据结构实现动态逻辑
解决深度学习模型训练初期异常高损失与完美验证准确率问题
纯CSS与HTML网格布局的HTML精简策略:SVG与JS方案解析
如何在低配置电脑上搭建轻量级J*a环境_占用更小的环境选择技巧
c++如何使用折叠表达式(Fold Expressions)_c++17可变参数模板新技巧
win11开机启动修复循环怎么办 Win11无法进入系统高级启动解决方法【修复】
如何设置Windows Defender的定时扫描_计划任务实现自动杀毒【安全】
PHP教程:将数据库查询结果动态展示到HTML Textarea的最佳实践
Golang如何优雅处理error_Golang error处理最佳实践总结
AO3最新可访问网址 Archive of Our Own官方在线入口
Pandas DataFrame 高效批量赋值:告别循环与笛卡尔积误区
12306选座怎么选到特殊座位_12306特殊座位选择注意事项
《刺客信条4:黑旗》重制版新细节曝光:无缝加载 地图更细致!
python3时间如何用calendar输出?
自定义 WooCommerce 购物车:始终显示全部交叉销售商品
在Qt QML中通过Python字典动态更新TextEdit内容的教程
限制HTML日期输入框的日期选择范围
mcjs网页版在线存档 mcjs云存档登录入口
c++如何实现一个简单的软件渲染器_c++从零开始的3D图形学
mcjs网页版流畅运行 mcjs低配电脑畅玩入口
Yandex搜索引擎官方地址 俄罗斯网络世界的主要入口
内存疯狂猛猛涨价:主板销量直接腰斩!
c++如何使用chrono库处理时间_c++标准库时间与日期操作
Win10怎么设置静态IP地址 Win10手动配置IP地址步骤【指南】
Android Studio计算器C键功能异常排查与修复教程