JPA关系更改未在PreUpdate回调中更新

时间:2012-09-28 14:12:19

标签: spring jpa eclipselink aspectj

在我目前的项目中,我有一个可以发布到其他系统的实体。为了跟踪出版物,实体具有称为“出版物”的关系。我正在使用Eclipselink。

此实体bean还具有“PreUpdate”注释方法。

为了能够使其他系统数据保持最新,我创建了一个围绕调用PreUpdate方法执行的Aspect。根据更改的属性,我需要删除一些出版物。一切都工作得很好。

我遇到的问题是portal-publishing组件正确发送删除命令并从实体“publications”列表中删除发布。我甚至可以在变更集中看到JPA已经注意到“发布”属性已经改变了。刷新事务后,缓存的实体不再具有已删除的发布。不幸的是,数据库仍然存在,当系统重新启动或者再次从DB加载实体时,再次出现了发布元数据。

我尝试了所有的一切。我甚至设法从Aspect中的JPA ChangeSet中获取已删除的实例,并尝试使用entityManager手动删除它们,但实际上没有任何效果。我似乎无法删除这些关系实体。目前我正在考虑使用JDBC删除它们,但这只是我的最后一项措施。

@Transactional
@Around("execution(* de.cware.services.truck.model.Truck.jpaPreUpdate(..))")
public Object truckPreUpdate(final ProceedingJoinPoint pjp) throws Throwable {
    if (alreadyExecutingMarker.get() != Boolean.TRUE) {
        alreadyExecutingMarker.set(Boolean.TRUE);

        final Truck truck = (Truck) pjp.getTarget();

        final JpaEntityManager jpaEntityManager = (JpaEntityManager) entityManager.getDelegate();
        final UnitOfWorkChangeSet changeSet = jpaEntityManager.getUnitOfWork().getCurrentChanges();
        final ObjectChangeSet objectChangeSet = changeSet.getObjectChangeSetForClone(truck);

        if (log.isDebugEnabled()) {
            log.debug("--------------------- Truck pre update check (" + truck.getId() + ") ---------------------");
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // If the truck date has changed, revoke all publication copies.
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////

        final ChangeRecord truckFreeDate = objectChangeSet.getChangesForAttributeNamed("lkwFreiDatum");
        if (truckFreeDate != null) {
            if (log.isDebugEnabled()) {
                log.debug("The date 'truckFreeDate' of truck with id '" + truck.getId() + "' has changed. " +
                        "Revoking all publications that are not marked as main applications");
            }

            for (final String portal : truck.getPublishedPortals()) {
                if (log.isDebugEnabled()) {
                    log.debug("- Revoking publications of copies to portal: " + portal);
                }

                portalService.deleteCopies(truck, portal);

                // Get any deleted portal references and use the entityManager to finally delete them.
                changeSet = jpaEntityManager.getUnitOfWork().getCurrentChanges();
                objectChangeSet = changeSet.getObjectChangeSetForClone(truck);
                final ChangeRecord publicationChanges = objectChangeSet.getChangesForAttributeNamed("publications");
                if (publicationChanges != null) {
                    if (publicationChanges instanceof CollectionChangeRecord) {
                        final CollectionChangeRecord collectionChanges =
                                (CollectionChangeRecord) publicationChanges;
                        @SuppressWarnings("unchecked")
                        final Collection<ObjectChangeSet> removedPublications =
                                (Collection<ObjectChangeSet>)
                                        collectionChanges.getRemoveObjectList().values();
                        for (final ObjectChangeSet removedPublication : removedPublications) {
                            final TruckPublication publication = (TruckPublication) ((org.eclipse.persistence.internal.sessions.ObjectChangeSet) removedPublication).getUnitOfWorkClone();
                            entityManager.remove(publication);
                        }
                    }
                }
            }
        }
    }
}

克里斯

3 个答案:

答案 0 :(得分:2)

问题是PreUpdate是在提交过程中引发的,当时已经计算了一组更改,并且已经计算了要删除的对象集。

理想情况下,您可以在应用程序逻辑中执行类似的操作,而不是通过持久性事件。

您可以尝试直接从您的事件执行DeleteObjectQuery(而不是使用em.remove()),这可能会有效,但通常最好在您的应用程序中执行此逻辑。

jpaEntityManager.getUnitOfWork()DeleteObject的(对象);

另请注意,getCurrentChanges()计算更改,在PreUpdate事件中已经计算了更改,因此您应该能够使用getUnitOfWorkChangeSet()。

答案 1 :(得分:0)

我找到的唯一解决方案是创建一个新的方法来执行删除并强制JPA创建一个新的Transaction。因此我失去了changeSet,我不得不手动找出哪些出版物被删除了。然后我只是调用那个帮助器方法并正确删除了这些出版物,但我发现这个解决方案非常难看。

@Transactional @Around(“execution(* de.cware.services.truck.model.Truck.jpaPreUpdate(..))”) public Object truckPreUpdate(final ProceedingJoinPoint pjp)抛出Throwable {     if(alreadyExecutingMarker.get()!= Boolean.TRUE){         alreadyExecutingMarker.set(Boolean.TRUE);

    final Truck truck = (Truck) pjp.getTarget();

    final JpaEntityManager jpaEntityManager = (JpaEntityManager) entityManager.getDelegate();
    final UnitOfWorkChangeSet changeSet = jpaEntityManager.getUnitOfWork().getCurrentChanges();
    final ObjectChangeSet objectChangeSet = changeSet.getObjectChangeSetForClone(truck);

    if (log.isDebugEnabled()) {
        log.debug("--------------------- Truck pre update check (" + truck.getId() + ") ---------------------");
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // If the truck date has changed, revoke all publication copies.
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////

    final ChangeRecord truckFreeDate = objectChangeSet.getChangesForAttributeNamed("lkwFreiDatum");
    if (truckFreeDate != null) {
        if (log.isDebugEnabled()) {
            log.debug("The date 'truckFreeDate' of truck with id '" + truck.getId() + "' has changed. " +
                    "Revoking all publications that are not marked as main applications");
        }

        removeCopyPublications(truck);
    }
}


@Transactional(propagation = Propagation.REQUIRES_NEW)
protected void removeCopyPublications(Truck truck) {
    // Delete all not-main-publications.
    for (final String portal : truck.getPublishedPortals()) {
        if (log.isDebugEnabled()) {
            log.debug("- Revoking publications of copies to portal: " + portal);
        }

        final Map<Integer, TruckPublication> oldPublications = new HashMap<Integer, TruckPublication>();
        for(final TruckPublication publication : truck.getPublications(portal)) {
            oldPublications.put(publication.getId(), publication);
        }

        portalService.deleteCopies(truck, portal);

        for(final TruckPublication publication : truck.getPublications(portal)) {
            oldPublications.remove(publication.getId());
        }

        for (TruckPublication removedPublication : oldPublications.values()) {
            if(!entityManager.contains(removedPublication)) {
                removedPublication = entityManager.merge(removedPublication);
            }
            entityManager.remove(removedPublication);
            entityManager.flush();
        }
    }
}

为什么我的第一个版本不起作用?

答案 2 :(得分:0)

我有一个类似的问题,我有一个类及其子项,当我从父项中删除子项时,它们从DB中删除,然后我在类Parent上使用merge附加新子项( CascadeType.ALL )使用JPA / EclipseLink,然后孩子们没有在DB上创建,而是在持久性Motor(JPA)中创建。我解决了这个问题:

1-我在 persistence.xml 文件中将 shared-cache-mode 设置为 NONE

2-当我移除孩子时,我立即执行了这个:

public void remove(T entity) {
        getEntityManager().remove(getEntityManager().merge(entity));
        getEntityManager().getEntityManagerFactory().getCache().evictAll();
    }

这就是全部。我希望这会有所帮助。

检查参考

http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching