我在Spring Boot应用程序中从聚合根发布事件时遇到问题。我基本上想要的是每次有关某人的某些信息发生变化时发布“更新”事件。 这个代码非常简单:
@Entity
public class Person {
@Transient
private final Collection<AbstractPersonRelatedEvent> events = new ArrayList<>();
Person(Person other) {
// copy other fields
other.events.foreach(events::add);
}
// other stuff
public Person updateInformation(...) {
Person updated = new Person(this);
// setting new data on the updated person
if (!hasUpdateEventRegistered()) {
updated.registerEvent(PersonDataUpdatedEvent.forPerson(updated));
}
return updated;
}
void registerEvent(AbstractPersonRelatedEvent event) {
events.add(event);
}
@DomainEvents
Collection<AbstractPersonRelatedEvent> getModificationEvents() {
return Collections.unmodifiableCollection(events);
}
@AfterDomainEventPublication
void clearEvents() {
events.clear();
}
}
我通过经理管理Person
个实例:
@Service
@Transactional
class PersistentPersonManager implements PersonManager {
// other methods are omitted
@Override
public Person save(Person person) {
return personRepository.save(person);
}
}
然而,当我打电话给经理时(manager.save(person.updateInformation(...))
事件似乎“失踪”:
在调用save()
方法时,所有事件仍然存在,但是当Spring调用getModificationEvents()
时,该集合为空。事件似乎在两者之间消失了(只执行了Spring代码)。
由于这是非常基本的,我必须遗漏一些必要的东西,但却陷入了困境。
那我该如何回到正轨呢?
答案 0 :(得分:2)
我假设你在这里使用JPA。
对于JPA,save
操作实际上在JPA merge
上执行了EnityManager
。
对于分离的实体merge
从数据库或当前会话加载/查找具有相同id的实体,并复制所有(已更改的)字段。这确实会忽略事件等瞬态字段。
您正在处理已分离的实体,因为每次调用updateInformation
时都要创建一个新实体。
所以这就是发生的事情:
您从数据库加载实体( e1 )。它没有注册任何事件。
通过调用updateInformation
,您可以创建一个新的分离实体( e2 )。您还可以使用 e2 注册活动。
调用save
时,JPA会找到匹配的 e1 并将所有更改从 e2 复制到其中,但事件除外。所以 e1 仍然没有注册事件。
事件被触发,但没有任何因为只使用 e1 。
为了解决此问题:请勿在{{1}}中创建实体的新实例。