Hibernate中@ManyToMany关联返回空Set,改为List则正常
问题原因及解决办法
核心原因
这个问题的本质是HashSet依赖equals()和hashCode()方法判断元素唯一性,而你的实体类在这两个方法的实现上存在问题,导致JPA加载关联数据时无法正确识别元素,最终集合为空。
具体细节:
- Lombok的
@Data注解会自动基于所有字段生成equals()和hashCode(),这包含了非id字段(比如version、业务字段)。当Hibernate通过FetchType.EAGER加载关联的TaxonomyElement时,可能先创建代理对象,或加载过程中字段未完全初始化,导致equals/hashCode判断出错,认为元素已存在于HashSet中(或无法匹配),从而不添加到集合里。 - 你用
final修饰集合字段并直接初始化new HashSet<>(),虽然JPA允许这种写法,但Hibernate原本会替换集合为自身的PersistentSet实现(用于跟踪集合变化),final修饰可能干扰这个替换过程,导致关联数据无法被正确填充。
而List是有序集合,不做唯一性校验,只要加载到数据就直接添加,因此不会出现这个问题。
解决办法
1. 修正equals()和hashCode()实现
推荐只基于实体的id字段实现这两个方法,因为id是实体的唯一标识,且持久化后状态稳定。可以用Lombok的@EqualsAndHashCode注解指定字段:
修改AuditedEntity:
@Data @Entity @SuperBuilder @AllArgsConstructor @RequiredArgsConstructor @Inheritance(strategy = InheritanceType.JOINED) @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class AuditedEntity { @Id @GenericGenerator(name = "uuid2", strategy = "uuid2") @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "uuid2") @Column(length = 36) @EqualsAndHashCode.Include private String id; @Version @Column(nullable = false) private Integer version; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinTable( name = "audited_entity_to_taxonomy_element", joinColumns = {@JoinColumn(name = "audited_entity_id")}, inverseJoinColumns = {@JoinColumn(name = "taxonomy_element_id")} ) private Set<TaxonomyElement> taxonomyElements = new HashSet<>(); }
修改TaxonomyElement:
@Data @Entity @Builder @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class TaxonomyElement { @Id @GenericGenerator(name = "uuid2", strategy = "uuid2") @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "uuid2") @Column(length = 36) @EqualsAndHashCode.Include private String id; @ManyToMany(mappedBy = "taxonomyElements", cascade = CascadeType.ALL) private Set<AuditedEntity> auditedEntities = new HashSet<>(); }
2. 移除集合字段的final修饰
让Hibernate可以正常替换集合为自身的持久化实现类(比如PersistentSet),确保关联数据能被正确填充到集合中。
3. 可选:显式指定Hibernate集合实现(不推荐)
如果需要保留final,可以显式使用Hibernate的集合实现,但会耦合框架API:
import org.hibernate.collection.spi.PersistentSet; // ... private final Set<TaxonomyElement> taxonomyElements = new PersistentSet();
验证方式
修改后重启应用,调用findById方法,检查taxonomyElements是否能正常加载关联数据。
内容的提问来源于stack exchange,提问作者R3qUi3M
相关产品推荐
相关产品推荐

