变更为多对多集合时未呼叫JPA EventListener方法?

mqxuamgl  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(116)

我在Spring Web应用程序中使用JPA和Hibernate作为实现。
我使用EntityListener来审计(Spring-data)和其他通知目的。一般来说,它工作得很好。但是,当对多对多集合/关系进行更改时,没有对实体本身的任何字段进行任何其他更改EventListener@PostUpdate事件不会被触发。
给予一个具体的例子:我有一个User和一个Role实体,它们之间有多对多关系,使用一个基础表。如果我转到我的用户管理GUI,并向用户添加(或删除)角色,而不更改任何其他内容,则不会触发“postUpdate”事件,也不会修改审计信息。
它有一些逻辑(在低级别),因为用户表或角色表都不更新,只有关系表更新。但是,关系本身没有建模为实体类,因此不可能对其应用审计,至少在JPA级别是这样,因此触发实体本身的更改事件是有意义的。
这是一个正常的JPA行为,还是特定于Hibernate?有什么解决方法吗?您会做什么来触发这个事件?有什么想法?
注意:我发现很少有人提到这个限制,甚至更少的解决方案:This SO question(没有有用的答案)和this post on Hibernate's forum也没有任何答案。

thigvfpy

thigvfpy1#

JPA规范对于集合的更改触发拥有它们的实体上的前/后更新回调非常不清楚。我所说的不清楚,是指完全沉默。事实上,IMO规范中的措辞暗示回调不应该发生,因为前/后更新应该在 * 为该实体 * 发送SQL UPDATE语句之前/之后发生。
现在,如果拥有实体是版本化的,Hibernate会将对它拥有的任何集合(实际上是任何属性)的更改视为在拥有实体上强制版本递增,这将触发实体的更新,而实体的更新又会触发更新后回调。

juzqafwq

juzqafwq2#

您可以使用特殊的钩子来捕捉这个动作。在我的例子中,我用EntityManagerFactory创建了一个EventIntegrator

@Service
@RequiredArgsConstructor
public class EventIntegrator {
    private final EntityManagerFactory entityManagerFactory;
    private final CustomPostCollectionUpdateEventListener postCollectionUpdateEventListener;
    @PostConstruct
    private void init() {
        SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
        EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
        registry.getEventListenerGroup(EventType.POST_COLLECTION_UPDATE)
            .appendListener(postCollectionUpdateEventListener);
}

然后我创建了一个'PostCollectionUpdateEventListener'的实现。

@Component
@RequiredArgsConstructor
public class CustomPostCollectionUpdateEventListener implements PostCollectionUpdateEventListener {
    @Override
    public void onPostUpdateCollection(PostCollectionUpdateEvent event) {
        if (event.getAffectedOwnerOrNull() != null) {
            var object = (AEntity<?>) event.getAffectedOwnerOrNull();
            Arrays.stream(object.getClass().getMethods())
                .filter(method -> method.isAnnotationPresent(PrePersist.class) && method.getParameterCount() == 0)
                .forEach(method -> {
                    try {
                        method.invoke(object);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        ExceptionUtils.throwUnsupportedOperationException("@PrePersis method was found, but cannot be called");
                    }
                });
            object.log(Event.UPDATE);
        }
    }
}

我还为PostUpdateEventListener创建了一个实现,因为有时我会捕获PostUpdateEvent而不捕获CollectionUpdateEvent
您可以在org.hibernate.event.spi.EventType中看到更多挂钩

相关问题