hibernate 更新后保留实体嵌套集合顺序

ffvjumwh  于 8个月前  发布在  其他
关注(0)|答案(1)|浏览(55)

我定义了一个模式,其中一个实体Event与另一个实体Person具有多对多关系。
Event的一侧,我根据Person的一个属性(在我的示例中为name)对Person的集合进行排序。
当我查询一个新的Event示例时,它工作得很好,但是当同一个实体被修改后,它就不工作了。相反,它将保留插入顺序,这是不正确的。
下面是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);
}

下面是一个测试,其中最后一个Assert由于意外行为而失败:

@Test
void addParticipant_addOne_oneMore() {
    var event = et.createEventWithParticipantsAs("[email protected]");
    var lastGuestRef = new AtomicReference<Person>();

    up.asUser("[email protected]", () -> {
        var lastGuest = new Person().setName("Bob");
        lastGuestRef.set(personRepo.save(lastGuest));
    });

    var lastGuest = lastGuestRef.get();

    var response = testRest.perform(
            POST,
            "/events/%s/participants/%s".formatted(event.getId(), lastGuest.getId()),
            null,
            PublicEvent.class,
            up.header("[email protected]")
    );

    assertThat(response.status()).isEqualTo(200);
    var participants = new ArrayList<>(event.getParticipants());
    participants.add(lastGuest);
    // Java and PostgreSQL lexicographical order is not quite the same, but it does not matter here
    participants.sort(Comparator.comparing(Person::getName));
    assertThat(response.body().participants().size()).isEqualTo(event.getParticipants().size() + 1);
    assertThat(response.body().participants()).isEqualTo(PersonCtrl.PublicPerson.from(participants));
}

请注意,我使用了一些自定义函数来创建用户并将其存储在SecurityContextManager中。希望测试逻辑对读者来说仍然是清楚的。
预期结果是:

[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]]

我知道这个问题来自于Hibernate将我的TreeSortedSet替换为PersistentSet,并且只在检索实体时应用@OrderBy
现在我想知道,如何以预期的顺序(尊重@OrderBy)检索具有嵌套集合的实体?

vecaoik1

vecaoik11#

通过搜索用户“M.代努姆”在评论中,我偶然发现了https://stackoverflow.com/a/42170280/7752551,它比较了@OrderBy@SortNatural
前者在我的用例中失败了,因为Hibernate @OrderBy注解在SQL级别暗示了相应的ORDER BY子句。此外,它不关心Java级别的任何操作。因此,任何涉及上述列表的java代码都必须自己处理其顺序。特别令人烦恼的是,Java和SQL的字典顺序是不一样的(想想重读的字母)。
第二个选项是使用SortNaturalSortNatural使用嵌套实体实现的接口Comparable。这种解决方案意味着排序完全在Java级别处理。效率不高,但至少是一致的。一个类似的选项是@SortComparator,它的行为完全相同,但需要提供Comparator,而不是使用嵌套实体实现Comparable

相关问题