Eclipse Texo ModelEMFConverter和Hibernate代理

时间:2015-07-10 09:38:26

标签: java hibernate emf

我试图将Eclipse Texo集成到我现有的Hibernate项目中。我已经在ECore中建模了我的域模型,并使用Texo和常规的EMF代码生成从那里生成了EMF和POJO代码。

存储在数据库中的获取实体(POJO)没有问题,现在我想使用Texo的ModelEMFConverter将Hibernate映射的数据模型转换为相应的EMF模型。但是,由于Hibernate返回的实体被透明代理,此尝试失败。 Texo的ModelResolver无法查找这些代理实体的模型描述符,因为它将实体的类(代理类)与映射的类进行比较,并在我的情况下失败并出现异常:

  

线程中的异常" main" java.lang.IllegalStateException:该类   类   foob​​ar.Entity _ $$ _ jvst4f2_5   不是由此ModelResolver管理的   org.eclipse.emf.texo.utils.Check.isNotNull(Check.java:66)at   org.eclipse.emf.texo.model.ModelResolver.getModelDescriptor(ModelResolver.java:366)     在   org.eclipse.emf.texo.model.ModelResolver.getModelObject(ModelResolver.java:298)     在   org.eclipse.emf.texo.resolver.DefaultObjectResolver.toUri(DefaultObjectResolver.java:188)     在   org.eclipse.emf.texo.resolver.DefaultObjectResolver.resolveToEObject(DefaultObjectResolver.java:98)     在   org.eclipse.emf.texo.converter.ModelEMFConverter.createTarget(ModelEMFConverter.java:146)     在   org.eclipse.emf.texo.converter.ModelEMFConverter.convertSingleEReference(ModelEMFConverter.java:265)     在   org.eclipse.emf.texo.converter.ModelEMFConverter.convertContent(ModelEMFConverter.java:189)     在   org.eclipse.emf.texo.converter.ModelEMFConverter.convert(ModelEMFConverter.java:107)   [...]

来自ModelResolver的相关代码位:

  public ModelObject<?> getModelObject(final Object target) {
    /* ... snip ... */

    final ModelDescriptor modelDescriptor = getModelDescriptor(target.getClass(), true);
    return modelDescriptor.createAdapter(target);
  }

我尝试使用以下代码在将代理实体传递给模型转换器之前手动展开代理实体:

    final List<Object> objects = entities
            .stream()
            .map(o ->
                o instanceof HibernateProxy ?
                    (Entity) ((HibernateProxy) o).getHibernateLazyInitializer().getImplementation() : o)
            .collect(Collectors.toList());

    final ModelEMFConverter converter = new ModelEMFConverter();
    final Collection<EObject> eObjects = converter.convert(objects);

理论上,这种方法似乎有效(我通过单步执行转换代码进行检查),但是对于我的数据模型中的关联所引用的实体,它没有包含在原始entities列表中。我想避免为了摆脱代理而不得不手动遍历整个对象图。

有没有办法从Hibernate中检索未经过代理的实体?或者是否有人可能会建议我如何从不同的角度进行模型转换?

提前感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

您可以编写一个通用的替换器,它将遍历整个图并替换给定实体实例的所有代理,如下所示:

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.hibernate.Hibernate;
import org.hibernate.proxy.HibernateProxy;

public class HibernateProxyReplacer {

    @SuppressWarnings("unchecked")
    public <T extends Entity> T replaceProxies(T entity) {
        try {
            return (T) replaceProxies(entity, new ArrayList<Object>());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    private Object replaceProxies(Object entity, List<Object> processedObjects) throws Exception {
        entity = getImplementation(entity);
        if (isProcessed(entity, processedObjects)) {
            return entity;
        }
        processedObjects.add(entity);

        for (Field field : getDeclaredFields(entity)) {
            if (isStaticOrFinal(field)) {
                continue;
            }
            field.setAccessible(true);
            Object value = field.get(entity);
            if (value == null) {
                continue;
            }
            Hibernate.initialize(value);
            if (value instanceof Collection) {
                replaceProxiesInCollection((Collection<Object>) value, processedObjects);
            } else if (value instanceof Entity) {
                field.set(entity, replaceProxies(value, processedObjects));
            }
        }

        return entity;
    }

    private Object getImplementation(Object object) {
        return object instanceof HibernateProxy ? ((HibernateProxy) object).getHibernateLazyInitializer().getImplementation() : object;
    }

    private boolean isStaticOrFinal(Field field) {
        return ((Modifier.STATIC | Modifier.FINAL) & field.getModifiers()) != 0;
    }

    private List<Field> getDeclaredFields(Object object) {
        List<Field> result = new ArrayList<Field>(Arrays.asList(object.getClass().getDeclaredFields()));
        for (Class<?> superclass = object.getClass().getSuperclass(); superclass != null; superclass = superclass.getSuperclass()) {
            result.addAll(Arrays.asList(superclass.getDeclaredFields()));
        }
        return result;
    }

    private void replaceProxiesInCollection(Collection<Object> collection, List<Object> processedObjects) throws Exception {
        Collection<Object> deproxiedCollection = new ArrayList<Object>();
        for (Object object : collection) {
            deproxiedCollection.add(replaceProxies(object, processedObjects));
        }
        collection.clear();
        collection.addAll(deproxiedCollection);
    }

    private boolean isProcessed(Object object, List<Object> processedObjects) {
        for (Object processedObject : processedObjects) {
            // Intentional comparison by reference to avoid relying on equals/hashCode
            if (processedObject == object) {
                return true;
            }
        }
        return false;
    }
}

不要忘记回滚完成此操作的事务(Hibernate可能认为对象很脏,因为我们手动更改了字段值)。或者将其设置为只读(通过将刷新模式设置为手动)。或者在不刷新会话的情况下明确清除会话,以便分离的图形变得分离。

如果这是您的要求的障碍,那么您可以通过从托管实体实例读取值并将deproxied值设置为另一个实例来更改此方法。这样,您就可以构建一个新的单独的非托管实体实例,其整个图形在没有任何代理的情况下进行初始化。

或者,您可以只存储有关必要更改的信息,然后在分离的实例上将其应用于事务之外,例如:

commands.add(new ReplaceFieldCommand(field, entity, deproxiedObject));
commands.add(new ReplaceCollectionCommand(collection, entity, deproxiedCollection));