更新实体后如何保留嵌套集合的排序?
问题
Event与Person为多对多关系,在Event侧通过@OrderBy注解和TreeSet按Person的name属性对参与者集合排序。查询新Event实例时排序正常,但修改同一实体后,集合保留插入顺序而非预期排序。已知Hibernate将TreeSet替换为PersistentSet,仅在实体查询时应用@OrderBy,如何获取符合@OrderBy规则排序的嵌套集合?
相关代码
Event实体
@Entity public class Event { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; private String name; private LocalDate occursAt; @ManyToMany @OrderBy("name") private Set<Person> participants = new TreeSet<>(Comparator.comparing(Person::getName)); }
Person实体
@Entity public class Person { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; private String name; @ManyToMany(mappedBy = "participants") @OrderBy("name") private Set<Event> events = new TreeSet<>(Comparator.comparing(Event::getName)); }
添加参与者接口方法
@PostMapping("/{event}/participants/{participant}") public PublicEvent addParticipant(@PathVariable UUID event, @PathVariable UUID participant) { var originalEvent = eventRepo.getReferenceById(event); var originalPerson = personRepo.getReferenceById(participant); originalPerson.getEvents().add(originalEvent); originalEvent.getParticipants().add(originalPerson); personRepo.save(originalPerson); return PublicEvent.from(originalEvent); }
测试结果
预期排序结果
[PublicPerson[id=05572a20-f82c-4d16-81f3-f41ccd45ac89, name=Alexandre], PublicPerson[id=955f6354-7699-4b18-8825-80808bacf7e4, name=Benoit], PublicPerson[id=8f1ec49d-530c-4ea8-bb20-555f4119d96c, name=Bob], PublicPerson[id=a7fb90ba-06f4-432e-92c9-65c968d4b34c, name=Coraline], PublicPerson[id=ca4d0019-9c3b-4ed7-a446-8bfb7f9ba58a, name=Denise], PublicPerson[id=e20da141-304a-4baa-9cbf-bc9a77435869, name=Eleonore], PublicPerson[id=6bd739c6-c977-4567-95c8-f8ddd60dff08, name=Frank]]
实际排序结果
[PublicPerson[id=05572a20-f82c-4d16-81f3-f41ccd45ac89, name=Alexandre], PublicPerson[id=955f6354-7699-4b18-8825-80808bacf7e4, name=Benoit], PublicPerson[id=a7fb90ba-06f4-432e-92c9-65c968d4b34c, name=Coraline], PublicPerson[id=ca4d0019-9c3b-4ed7-a446-8bfb7f9ba58a, name=Denise], PublicPerson[id=e20da141-304a-4baa-9cbf-bc9a77435869, name=Eleonore], PublicPerson[id=6bd739c6-c977-4567-95c8-f8ddd60dff08, name=Frank], PublicPerson[id=8f1ec49d-530c-4ea8-bb20-555f4119d96c, name=Bob]]
解决方案
方法1:改用SortedSet维护内存排序
Hibernate支持SortedSet接口,将集合声明为SortedSet而非Set,Hibernate会使用PersistentSortedSet,既遵循数据库层面的@OrderBy,也会在内存中通过初始化的TreeSet比较器维护排序。
修改Event实体的集合定义:
@Entity public class Event { // ...其他字段 @ManyToMany @OrderBy("name") private SortedSet<Person> participants = new TreeSet<>(Comparator.comparing(Person::getName)); }
Person实体同理修改:
@Entity public class Person { // ...其他字段 @ManyToMany(mappedBy = "participants") @OrderBy("name") private SortedSet<Event> events = new TreeSet<>(Comparator.comparing(Event::getName)); }
方法2:保存后重新查询实体
getReferenceById返回的是代理对象,修改后直接返回可能读取的是内存中未排序的集合。保存后强制刷新并重新查询实体,Hibernate会从数据库加载按@OrderBy排序后的集合:
@PostMapping("/{event}/participants/{participant}") public PublicEvent addParticipant(@PathVariable UUID event, @PathVariable UUID participant) { var originalEvent = eventRepo.getReferenceById(event); var originalPerson = personRepo.getReferenceById(participant); originalPerson.getEvents().add(originalEvent); originalEvent.getParticipants().add(originalPerson); personRepo.save(originalPerson); // 刷新持久化上下文,确保数据写入数据库 eventRepo.flush(); // 重新查询完整的Event实体 var updatedEvent = eventRepo.findById(event).orElseThrow(); return PublicEvent.from(updatedEvent); }
方法3:DTO转换时手动排序
如果不想修改实体集合类型,可在转换为PublicEvent的过程中,对参与者集合手动排序:
// 修改PublicEvent.from方法 public static PublicEvent from(Event event) { PublicEvent dto = new PublicEvent(); // 赋值其他字段 // 对参与者按name排序后转换为DTO List<PublicPerson> sortedParticipants = event.getParticipants().stream() .sorted(Comparator.comparing(Person::getName)) .map(PublicPerson::from) .collect(Collectors.toList()); dto.setParticipants(sortedParticipants); return dto; }
内容的提问来源于stack exchange,提问作者Adrien H
相关产品推荐
相关产品推荐

