Hibernate中基于自动生成ID的对象存入Set仅添加单个元素的解决方案咨询
解决方案:无需提前save即可将未持久化Hibernate实体存入Set
这确实是Hibernate实体使用基于ID的equals/hashCode时的常见痛点——未持久化的对象ID都是默认值(这里是0),导致HashSet会把它们判定为同一个对象。不用每次创建都调用session.save()的话,有几个实用的变通方案:
方案1:用IdentityHashMap实现Set
最简单的办法是用IdentityHashMap来构建Set,因为它是基于对象引用(==)而非equals/hashCode来判断元素是否重复的,完全绕开了基类的ID依赖。
代码示例:
// 用IdentityHashMap创建一个基于引用判断的Set Set<Employee> set = Collections.newSetFromMap(new IdentityHashMap<>()); set.add(new Employee("lala")); set.add(new Employee("baba")); // 此时Set中会包含两个不同的Employee实例
这个方案的优势是零代码侵入,不用改实体类也不用写额外包装。需要注意的是:后续如果把这些对象持久化(ID变化),只要对象引用没变,它们在Set里依然会被当作同一个元素,这点符合大多数场景的预期。
方案2:创建临时包装类兼容未持久化状态
如果需要兼顾“未持久化时区分不同实例”和“持久化后基于ID判断相等”的需求,可以写一个包装类来覆盖equals/hashCode逻辑,避开基类的限制。
包装类示例:
public class EmployeeWrapper { private final Employee employee; public EmployeeWrapper(Employee employee) { this.employee = employee; } // 提供获取原对象的方法 public Employee getEmployee() { return employee; } @Override public int hashCode() { // 未持久化时:用name+对象引用的哈希值(避免同name的不同实例被判定为重复) if (employee.getId() == 0) { return Objects.hash(employee.getEm_name(), System.identityHashCode(employee)); } // 持久化后:复用基类的ID哈希逻辑 return Long.hashCode(employee.getId()); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EmployeeWrapper that = (EmployeeWrapper) o; // 未持久化时:比较name+对象引用 if (employee.getId() == 0 && that.employee.getId() == 0) { return Objects.equals(employee.getEm_name(), that.employee.getEm_name()) && System.identityHashCode(employee) == System.identityHashCode(that.employee); } // 持久化后:比较ID return employee.getId() == that.employee.getId(); } }
使用方式:
Set<EmployeeWrapper> set = new HashSet<>(); set.add(new EmployeeWrapper(new Employee("lala"))); set.add(new EmployeeWrapper(new Employee("baba"))); // 后续需要操作Employee时,调用getEmployee()即可 for (EmployeeWrapper wrapper : set) { Employee emp = wrapper.getEmployee(); // 处理逻辑 }
这个方案稍微麻烦一点,但能完美兼容未持久化和持久化后的场景,适合需要长期维护的集合逻辑。
额外提醒
其实Hibernate官方并不推荐仅用ID来实现equals/hashCode(因为未持久化对象的ID是未初始化的),不过你现在没法修改基类,所以上面的变通方案是最合适的选择。
内容的提问来源于stack exchange,提问作者Daniela
相关产品推荐
相关产品推荐

