Spring Boot 3 @Transactional测试失败问题求助
问题原因分析
- 不可变集合触发Hibernate操作异常:你的
Agency实体aggregate方法中,使用了Java 16+的Stream.toList(),该方法返回的是不可修改的List(底层基于Collections.unmodifiableList实现)。 - 事务上下文的脏检查机制冲突:添加
@Transactional注解后,整个测试方法处于同一个事务中,Hibernate会在事务提交前对持久化状态的Agency对象执行脏检查。对于@ElementCollection标注的集合属性,Hibernate默认会尝试清空原集合再插入新元素,但不可变List不支持clear()等修改操作,直接抛出UnsupportedOperationException。 - 无事务时的特殊场景:不添加
@Transactional时,创建、更新等操作都是独立的短事务,对象在save后就脱离了Hibernate的持久化上下文,不会触发后续的集合修改检查,因此测试能通过,但会残留测试数据污染数据库。
解决方案
提供三种可行的解决方式,按需选择:
方案一:生成可变集合替换不可变集合
在aggregate方法中,将Stream.toList()替换为Collectors.toCollection(ArrayList::new),生成支持修改的ArrayList:
public void aggregate(AgencyUpdateDto agencyUpdateDto){ if (agencyUpdateDto.getName() != null) name = agencyUpdateDto.getName(); if (agencyUpdateDto.getSupportContactList() != null) supportContact = agencyUpdateDto.getSupportContactList().stream() .map(SupportContactFactory::create) .collect(Collectors.toCollection(ArrayList::new)); if (agencyUpdateDto.getNotificationEmailList() != null) notificationEmail = agencyUpdateDto.getNotificationEmailList().stream() .map(NotificationEmailFactory::create) .collect(Collectors.toCollection(ArrayList::new)); // 其他属性赋值省略 }
方案二:初始化实体集合为可变类型
在Agency实体中,直接将集合属性初始化为可变的ArrayList,确保Hibernate加载的集合本身支持修改:
@ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "agency_support_contact", joinColumns=@JoinColumn(name = "agency_id")) private List<SupportContact> supportContact = new ArrayList<>(); @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "agency_notification_email", joinColumns=@JoinColumn(name = "agency_id")) private List<NotificationEmail> notificationEmail = new ArrayList<>();
方案三:修改原集合而非替换集合
不直接给集合属性赋值,而是清空原集合后添加新元素,避免替换成不可变集合:
public void aggregate(AgencyUpdateDto agencyUpdateDto){ if (agencyUpdateDto.getName() != null) name = agencyUpdateDto.getName(); if (agencyUpdateDto.getSupportContactList() != null) { supportContact.clear(); supportContact.addAll(agencyUpdateDto.getSupportContactList().stream() .map(SupportContactFactory::create) .toList()); } if (agencyUpdateDto.getNotificationEmailList() != null) { notificationEmail.clear(); notificationEmail.addAll(agencyUpdateDto.getNotificationEmailList().stream() .map(NotificationEmailFactory::create) .toList()); } // 其他属性赋值省略 }
内容的提问来源于stack exchange,提问作者maik
相关产品推荐
相关产品推荐

