JPA在两个不同的模式和会话之间复制项目

时间:2017-11-09 13:46:18

标签: java spring hibernate jpa

我以为我会分享我的见解,可能会在两个不同的模式之间复制实体(每个模式都由自己的dataSource / entityManager / Session处理)。

在我的情况下,目标架构可能有也可能没有该项,因此它可能是插入或更新。
此外,虽然id是自动生成的,但我希望保留源ID(避免目标模式生成自己的id)。

大部分辛勤工作,都是由以下人员处理的:
    Session.replicate(sourceEntity,ReplicationMode.OVERWRITE)

然而,我遇到的挑战是:

  1. 必须调用 detach() 方法以避免出现此错误: org.hibernate.HibernateException:非法尝试将集合与两个关联公开会议
  2. 当复制现有项目时,如果从其某些关联子项中删除了较新的等效源项目,则目标实体仍然保留过时的子项 - 因此我在复制源之前删除了旧的目标实体。
  3. 我在自动生成的ID中遇到了另一个问题。虽然可以预期复制方法会传输现有的Id,但事实并非如此,并且始终生成新的Id。我通过将sessionFactory的DefaultReplicateEventListener替换为自定义的一个来解决此问题,该自定义请求从源实体获取Id。
  4. 此代码处理副本:

    MyEntity sourceEntity = sourceEntityManager.find(MyEntity.class, sourceEntity.getId());
    sourceEntityManager.detach(sourceEntity);
    Session hibernateSession = 
    targetEntityManager.unwrap(Session.class);
    hibernateSession.replicate(sourceEntity, ReplicationMode.OVERWRITE);
    

    在复制之前首先删除现有实体来协助干净复制副本:

    MyEntity existingEntity = 
    targetEntityManager.find(MealDescription.class, sourceEntity.getId())
    targetEntityManager.remove(existingMeal);
    

    通过重写类并替换侦听器来替换默认的复制侦听器(DefaultReplicateEventListener):

    public static class IdPreservingReplicateEventListener extends DefaultReplicateEventListener implements Loggable {
        @Override
        protected Serializable performSaveOrReplicate(Object entity, EntityKey key, EntityPersister persister, boolean useIdentityColumn, Object anything, EventSource source, boolean requiresImmediateIdAccess){
            if (key == null) {
                Serializable id = persister.getIdentifier( entity, source );
                key = source.generateEntityKey( id, persister );
                useIdentityColumn = false;
            }
    
            return super.performSaveOrReplicate(entity, key, persister, useIdentityColumn, anything, source, true);
        }
    }
    

    将其替换为sessionFactory:

    EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
    registry.getEventListenerGroup(EventType.REPLICATE).clear();
    registry.getEventListenerGroup(EventType.REPLICATE).appendListener(new IdPreservingReplicateEventListener());
    

1 个答案:

答案 0 :(得分:1)

您可以尝试将其与SourceEntityManager分离并将其合并到TargetEntityManager:

final MyEntity sourceEntity = sourceEntityManager.find(MyEntity.class, sourceEntity.getId());
sourceEntityManager.detach(sourceEntity);
targetEntityManager.merge(sourceEntity);

有关详细信息,请参阅此答案https://stackoverflow.com/a/45734649/7634201